ವಿಕಿಪೀಡಿಯ tcywiki https://tcy.wikipedia.org/wiki/%E0%B2%AE%E0%B3%81%E0%B2%96%E0%B3%8D%E0%B2%AF_%E0%B2%AA%E0%B3%81%E0%B2%9F MediaWiki 1.47.0-wmf.16 first-letter ಮಾದ್ಯಮೊ ವಿಸೇಸೊ ಪಾತೆರ ಬಳಕೆದಾರೆ ಬಳಕೆದಾರೆ ಪಾತೆರ ವಿಕಿಪೀಡಿಯ ವಿಕಿಪೀಡಿಯ ಪಾತೆರ ಫೈಲ್ ಫೈಲ್ ಪಾತೆರ ಮಾದ್ಯಮೊವಿಕಿ ಮಾದ್ಯಮೊವಿಕಿ ಪಾತೆರ ಟೆಂಪ್ಲೇಟ್ ಟೆಂಪ್ಲೇಟ್ ಪಾತೆರ ಸಕಾಯೊ ಸಕಾಯೊ ಪಾತೆರ ವರ್ಗೊ ವರ್ಗೊ ಪಾತೆರ ತಡ್ಯ ತಡ್ಯ ಪಾತೆರ ಕರಡು ಕರಡು ಪಾತೆರ TimedText TimedText talk ಮೋಡ್ಯೂಲ್ ಮೋಡ್ಯೂಲ್ ಪಾತೆರ Event Event talk ಕಲ್ಕುಡ 0 3782 366201 362381 2026-08-21T10:25:22Z ChiK 1136 Add: date, title. Changed bare reference to CS1/2. | [[:en:WP:UCB|Use this tool]]. [[:en:WP:DBUG|Report bugs]]. | #UCB_Gadget #proveit 366201 wikitext text/x-wiki '''ಕಲ್ಕುಡೆ''' [[ತುಳುನಾಡ್|ತುಲುನಾಡ್‍]]ದ ಆನ್ ಬೂತೊ. ಅಣ್ಣೆ ಕಲ್ಕುಡೆ ತಂಗಡಿ [[ಕಲ್ಲುರ್ಟಿ]]. ಮೊಕುಲು ಇರ್ವೆರ್ ಒಟ್ಟಿಗೆ ಆರಾಧನೆ ಪಡೆವೊನುವೆರ್. ಕಲ್ಕುಡೆ [[ಭೂತಾರಾಧನೆ|ಭೂತೊ]]ಲೆಡ್ ಪ್ರಮುಖವಾಯಿ ಭೂತೊ. ಪ್ರದಾನವಾದ್ ಕಲ್ಕುಡೆ ಒರಿ ಶಿಲ್ಪಿ. ಇಂಬ್ಯನ್ ಬೀರ ಕಲ್ಕುಡೇಂದ್ಲಾ ಪನ್ಪೆರ್. ಇಂಬ್ಯನ ಅಮ್ಮೆ ಸಂಬು ಕಲ್ಕುಡೆ, ಮಲ್ಲ ಸಿಲ್ಪಿ. ಬೆಳಗೊಳದ ಗೊಮ್ಮಟನ್ ಕೆತ್ತುವೆ. ಮಲ್ಲ ಕೆತ್ತನೆದ ಬೇಲೆನೇ ಮಲ್ಪುವೆ. ಆಂಡ ಈ ಕೆತ್ತನೆಡ್ ಆಯ ಮಗೆ ಆಯಿನ ಬೀರ ಕಲ್ಕುಡೆನೇ ದೊಸೊ ತೋಜಾಬೆ. ಇಂದೆಡ್ದಾದ್ ಅಮ್ಮೆ ಅವಮಾನೊಡು ಸಯಿಪೆ. ಬುಕೊ ಮಗೆನೇ ಅಮ್ಮನ ಕೆಲಸೊನು ಮುಂದುವರಿಸಾವೆ. ಬೈರರಸನ ಲೆಪ್ಪುಗು ಕಾರ್ಲಗ್ ಬತ್ತ್ ದ್ ಕಾರ್ಲಡ್ ಗೊಮ್ಮಟನ್ ನಿರ್ಮಾಣ ಮಲ್ಪುವೆ. ಇಂದೆನ್ ತೂದು ಕುಸಿ ಆಯಿನ ಬೈರರಸೆ ಎಲ್ಲೆ ಎನ್ನ ಅರಮನೆಡ್ ನಿಕ್ಕ್ ಸೇಜಪಾಲ್ ಕೊರ್ಪೆಂದ್ ಲೆಪುಡಾವೆ. ಅಲ್ಪ ನನ ದುಂಬುಗು ಬೇತೆ ಏರೆನಲ್ಪಲಾ ಈಂಚಿತ್ತಿ ಪೊರ್ಲುದ ಕೆತ್ತನೆದ ಕೆಲಸೊ ಮಲ್ಪಿಯೆರೆ ಬಲ್ಲಿಂದ್ ಕಲ್ಕುಡನ ಒಂಜಿ ಕಯಿ ಬುಕ್ಕೊ ಒಂಜಿ ಕಾರ್ ಕಡ್ಪುವೆ. ಬೀರ ಕಲ್ಕುಡನ ತಂಗಡಿ ಕಲ್ಲುರ್ಟಿ(ಕಾಳಮ್ಮ) ಪಲಯನ್ ನಾಡೊಂದು ಬತ್ತ್ ದ್ ಅಣ್ಣನ ಸ್ಥಿತಿ ತೂದು ಪ್ರಾಣ ದೆತೊಂದು ಮಾಯಕ ಆದ್ ಬಯಿರರಸಗ್ ತೊಂದರೆ ಕೊರ್ಪೆರ್. [[File:KALKUDA nema.jpg|thumb|ಕಲ್ಕುಡನ ನೇಮ]] == ಕಲ್ಕುಡೆ ಬೂತೊದ ವಿದೊ == # ಅನ್ನರ ಕಲ್ಕುಡೆ # ಆದೂರು ಕಲ್ಕುಡೆ<ref>{{cite web | title=ಆದೂರು ಕಲ್ಲುರ್ಟಿ-ಕಲ್ಕುಡ ದೈವಕ್ಕೆ ಆಟಿಯಲ್ಲೇ ಕೋಲ! | url=http://vijaykarnataka.indiatimes.com/articleshow/15464342.cms?prtpage=1 }}</ref> # ಸುಳ್ಯತಾ ಕಲ್ಕುಡೆ. # ಪುತ್ತೂರು ಕಲ್ಕುಡೆ. [[File:KALKUDA KOLA 2.jpg|thumb|ಕಲ್ಕುಡೆ]] == ಪುದರ್ ದ ಮೂಲ == '''ಕಲ್ಕುಡ''' ಪದೊ "ಕಲ್ಲ್" ಬುಕ್ಕ "ಕುಡ/ಕುಟ್ಟು" ಪದೊಲುಂಡ ಒಟ್ಟಾದ್ ಬತ್ತಿನೆರ್ ಪಂದ್ ನಂಬಿಕೆ ಉಂಡು. ಕಲ್ಲ್ ಕೆತ್ತುನ, ಶಿಲ್ಪಕಲೆ ಮಲ್ಪುನ ಜನಕುಲದೊಡನೆ ಈ ದೈವೊನ ಸಂಬಂಧ ಉಂಡು ಪಂದ್ ಜನಪದ ಸಂಪ್ರದಾಯೊಲೆ ಪನ್ಪೆರ್. == ಪುರಾಣ ಬೊಕ್ಕ ಐತಿಹ್ಯ == ಕಲ್ಕುಡೆನ ಪುರಾಣೊ ತುಳುನಾಡ್ದ ಪ್ರಸಿದ್ಧ ದೈವಕಥೆಲೆ ಒಂಜಿ. ಕಲ್ಕುಡೆ ಒರಿ ಅದ್ಭುತ ಶಿಲ್ಪಿ ಆಯಿನೆ. ಇಂಬೆನ ಅಮ್ಮೆ '''ಸಂಬು ಕಲ್ಕುಡೆ''' (ಮಲ್ಲ ಶಿಲ್ಪಿ) ಬೆಳಗೊಳದ ಗೊಮ್ಮಟೇಶ್ವರ ಮೂರ್ತಿನ ಕೆತ್ತನೆಡ್ ಪಾಲ್ಗೊಂಡಿನೆ ಪಂದ್ ಐತಿಹ್ಯ ಉಂಡು.<ref>{{cite web | title=ಇವರು 'ಮಾತನಾಡುವ ಕಲ್ಕುಡ' ! Beeru Panara – Avinash Kamath's Offical Website | url=https://avinashkamath.in/talking-kalkuda-beeru-panara/ }}</ref> ಅಮ್ಮೆನ ಮರಣಾನಂತರ ಮಗೆ ಆಯಿನ ಕಲ್ಕುಡೆ ಶಿಲ್ಪಕಲೆಡ್ ಅಸಾಮಾನ್ಯ ಪಾಂಡಿತ್ಯ ತೋರಾಯಿನೆ. ಬೈರರಸ (ಬೈರ ಅರಸ) ಎನ್ನ ರಾಜನ ಆಹ್ವಾನೊಡನೆ ಕಾರ್ಲ ಪ್ರದೇಶಡ್ ಭವ್ಯ ಶಿಲ್ಪಕೆಲಸೊನು ಮಲ್ಪುವೆ. ಕಲ್ಕುಡೆನ ಶಿಲ್ಪಕಲೆ ಬೊಕ್ಕ ಪ್ರತಿಭೆ ತೂದು ರಾಜಗ್ ಅಸೂಯೆ ಉಂಡಾಯಿನೆ ಪಂದ್ ಕಥೆ ಪನ್ಪೆರ್. ಅಸೂಯೆಡ್ ರಾಜ ಕಲ್ಕುಡೆನ ಒಂಜಿ ಕೈ ಬೊಕ್ಕ ಒಂಜಿ ಕಾರ್ ಕಡ್ಪುನ ಶಿಕ್ಷೆ ಕೊರ್ಪೆ. ಗಂಭೀರ ಗಾಯೊದೊಟ್ಟುಗು ಕಲ್ಕುಡೆ ಸೈತು ಪೋಪೆರ್.<ref>{{Cite web |url=https://kannada.news18.com/news/astrology/dakshina-kannada-interesting-story-of-tulunada-karanika-daiva-rks-cgl-local18-1996828.html |title=Dakshina Kannada: ಕಲ್ಲೇಗದ ಕಲ್ಕುಡ-ಕಲ್ಲುರ್ಟಿ ದೈವಗಳ ವೈಭವದ ನೇಮೋತ್ಸವ! |accessdate=2026-08-21 |website=kannada.news18.com |language=kn |archiveurl=http://web.archive.org/web/20250215100627/https://kannada.news18.com/news/astrology/dakshina-kannada-interesting-story-of-tulunada-karanika-daiva-rks-cgl-local18-1996828.html |archivedate=2025-02-15}}</ref> ಉಂದೆನ್ ತೂದು ತಂಗಡಿ ಕಲ್ಲುರ್ಟಿ ದುಃಖ ಬತ್ತುದು ಪ್ರಾಣ ತ್ಯಾಗ ಮಲ್ಪುವಲ್. ಅಣ್ಣೆ–ತಂಗಡಿ ಆತ್ಮೊಲು ದೈವಸ್ವರೂಪೊ ದೊಟ್ಟುಗು ಪ್ರಕಟವಾದ್ ಅನ್ಯಾಯ ಮಲ್ಪಿನಕಲೆಗ್ ಶಿಕ್ಷೆ ಬೊಕ್ಕ ಸತ್ಯವಂತರೆಗ್ ರಕ್ಷಣೆ ಕೊರ್ಪೆರ್ ಪಂದ್ ನಂಬಿಕೆ ಉಂಡು. == ಕಾರಣಿಕ ದೈವೊ == ಕಲ್ಕುಡೆ ತುಳುನಾಡ್ದ '''ಕಾರಣಿಕ ದೈವೊ'''ಲೆ ಒಂಜಿ ಪಂದ್ ಪರಿಗಣನೆ ಉಂಡು. ಕಾರಣಿಕ ಅರ್ಥ ಭವಿಷ್ಯದ ಸೂಚನೆ ಅತ್ತಂಡು ದೈವಿಕ ಸಂದೇಶ. ನೇಮ, ಕೋಲೊಲೆ ಕಲ್ಕುಡೆ ದೈವೊ ಕೊರ್ಪುನ ನುಡಿ, ಸಂದೇಶ, ಸೂಚನೆಲು ಗ್ರಾಮೊಲೆ ಜನೊಲು ಮಸ್ತ್ ಗೌರವೊದೊಟ್ಟುಗು ಸ್ವೀಕಾರ ಮನ್ಪುವೆರ್. == ಆರಾಧನೆ == ಕಲ್ಕುಡೆ–ಕಲ್ಲುರ್ಟಿ ದೈವೊಲು ದಕ್ಷಿಣ ಕನ್ನಡ, ಉಡುಪಿ ಬೊಕ್ಕ ಕಾಸರಗೋಡು ಜಿಲ್ಲೆದ ಅನೇಕ ದೈವಸ್ಥಾನೊಲೆ ಆರಾಧನೆ ಪಡೆವೆರ್. ಬಂಟ, ಬಿಲ್ಲವ, ಮೊಗವೀರ, ಪರವ, ಪಂಬಡ ಅಂಚಿನೋ ಏತೋ ಸಮುದಾಯೊಲು ಮೊಕ್ಲೆನ ಆರಾಧನೆ ಮಲ್ಪುವೆರ್. ನೇಮ, ಕೋಲ, ತಂಬಿಲ ಬೊಕ್ಕ ವಾರ್ಷಿಕ ಜಾತ್ರೆಲೆ ಕಲ್ಕುಡೆ ಆರಾಧನೆ ನಡಾಪುಂಡು. ದೈವೊ ಭಕ್ತೆರೆಗ್ ನ್ಯಾಯ, ರಕ್ಷಣೆ ಬೊಕ್ಕ ಆಶೀರ್ವಾದ ಕೊರ್ಪೆರ್ ಪಂದ್ ನಂಬಿಕೆ ಉಂಡು. == ಪ್ರಮುಖ ಕ್ಷೇತ್ರೊಲು == * ಅನ್ನರ ಕಲ್ಕುಡೆ * ಆದೂರು ಕಲ್ಕುಡೆ * ಸುಳ್ಯದ ಕಲ್ಕುಡೆ * ಪುತ್ತೂರ್ದ ಕಲ್ಕುಡೆ * ಕಡಬುದ ಕಲ್ಕುಡೆ * ಕಲ್ಲೇಗ್ ಕಲ್ಕುಡ ದೈವಸ್ಥಾನ == ದೈವೊದ ಗುಣಧರ್ಮೊಲು == * ಶಿಲ್ಪಿ ಬುಕ್ಕ ಕಾರಿಗರರೆನ ರಕ್ಷಕ ದೈವೊ * ನ್ಯಾಯ ಬೊಕ್ಕ ಸತ್ಯೊದ ಸಂಕೇತ * ಅನ್ಯಾಯೊದೊಟ್ಟು ಸೈತುದು ವೀರಪುರುಷೆ ಆದ್ ದೈವೀಕರಣ * ಕಾರಣಿಕ ನುಡಿ ಕೊರ್ಪುನ ದೈವೊ * ಅಣ್ಣೆ–ತಂಗಡಿ ಭಕ್ತಿ ಬೊಕ್ಕ ಕುಟುಂಬ ಬಾಂಧವ್ಯೊದ ಪ್ರತೀಕ == ಕಲ್ಕುಡೆ–ಕಲ್ಲುರ್ಟಿ ಕೋಲ == ಕಲ್ಕುಡೆ ಕೋಲೊಲೆ ದೈವ ಪಾತ್ರಿ ವಿಶಿಷ್ಟ ಅಲಂಕಾರ, ಮುಖವರ್ಣಿಕೆ ಬುಕ್ಕ ಆಯುಧೊಡನೆ ಕಾಣೆರ್. ಕೋಲೊಲೆ ದೈವೊ ಭಕ್ತೆರೆನ ಪ್ರಶ್ನೆಲೆ ಉತ್ತರ ಕೊರ್ಪೆರ್, ವಿವಾದೊಲು ಪರಿಹಾರ ಮಲ್ಪೆರ್ ಬುಕ್ಕ ಆಶೀರ್ವಾದ ಕೊರ್ಪೆರ್. ಕಲ್ಲುರ್ಟಿ ಪಾತ್ರಿ ಸ್ತ್ರೀಶಕ್ತಿ, ರಕ್ಷಣೆ ಬುಕ್ಕ ಧೈರ್ಯೊದ ಪ್ರತೀಕ ಆಯಿನೆ. == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಲ್ಕುಡೆ–ಕಲ್ಲುರ್ಟಿ ತುಳುನಾಡ್ದ ಜನಪದ ಸಾಹಿತ್ಯ, ಪಾಡ್ದನ, ದೈವಕಥೆ, ಯಕ್ಷಗಾನ ಪ್ರಸಂಗ ಬುಕ್ಕ ಭೂತಾರಾಧನೆ ಸಂಪ್ರದಾಯೊಲೆ ಮಹತ್ವದ ಸ್ಥಾನೊನು ಪಡೆದುಂಡು. ಮೊಕುಲು ತುಳುನಾಡ್ದ ನ್ಯಾಯ, ವೀರತೆ, ಶಿಲ್ಪಕಲೆ ಬೊಕ್ಕ ಜನಪದ ನಂಬಿಕೆಲೆ ಪ್ರತಿನಿಧಿಪೆರ್.<ref>{{Cite web |url=https://buntsnow.com/archives/12670 |title=ಕಲ್ಲುರ್ಟಿ ಮತ್ತು ಕಲ್ಕುಡ ಕಥೆ |date=7 November 2023 |accessdate=2026-08-21 |website=www.buntsnow.com {{!}} web-based media For Bunts Community |last=Now |first=Bunts |language=en-US}}</ref> == ಪಿದಾಯಿದ ಕೊಂಡಿಲು == * https://avinashkamath.in/talking-kalkuda-beeru-panara/ * https://buntsnow.com/archives/12670 * https://web.archive.org/web/20241125194130/https://kannada.news18.com/news/astrology/kadambu-is-famous-for-this-reason-in-dakshina-kannada-rks-cgl-local18-1928027.html * https://web.archive.org/web/20250215100627/https://kannada.news18.com/news/astrology/dakshina-kannada-interesting-story-of-tulunada-karanika-daiva-rks-cgl-local18-1996828.html * https://puttur.suddinews.com/ == ಉಲ್ಲೇಕೊಲು == {{Reflist}} [[ವರ್ಗೊ:ತುಳುನಾಡ್ದ ಸಂಸ್ಕೃತಿ]] [[ವರ್ಗೊ:ಭೂತೊಲು]] tczhgi32uezndu9iggreaho7n8478mp ಟೆಂಪ್ಲೇಟ್:Main 10 5142 366180 348370 2026-07-09T05:15:34Z en>Steel1943 0 Undid revision [[Special:Diff/1363280637|1363280637]] by [[Special:Contributions/Steel1943|Steel1943]] ([[User talk:Steel1943|talk]]) apparently should have tested it since it doesn't work 366180 wikitext text/x-wiki <includeonly>{{#invoke:Labelled list hatnote|labelledList|Main article|Main articles|Main page|Main pages}}</includeonly><noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> hz9saa715ercns6m73a0a1ud2ptzt26 366181 366180 2026-08-21T10:06:45Z ChiK 1136 ೧ revisions imported from [[:en:Template:Main]] 366180 wikitext text/x-wiki <includeonly>{{#invoke:Labelled list hatnote|labelledList|Main article|Main articles|Main page|Main pages}}</includeonly><noinclude> {{documentation}} <!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --> </noinclude> hz9saa715ercns6m73a0a1ud2ptzt26 ಟೆಂಪ್ಲೇಟ್:Copy edit 10 6529 366106 110637 2026-08-21T02:49:41Z ChiK 1136 366106 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{ambox | subst = <includeonly>{{subst:substcheck}}</includeonly> | name = ನಕಲ್ ಮಾಂತುದ್ ಸಂಪೊಲಿಪು | type = style | class = ambox-Copy_edit | image = [[Image:Acap.svg|40x40px|alt=|link=]] | issue = This {{{1|article}}} '''may require [[:en:Wikipedia:Basic copyediting|copy editing]] for {{{for|{{{reason|grammar, style, cohesion, tone, or spelling}}}}}}'''. | fix = You can assist by [{{SERVER}}{{localurl:{{NAMESPACE}}:{{PAGENAME}}|action=edit}} editing it]. | removalnotice = yes | cat = Wikipedia articles needing copy edit | date = {{{date|}}} | all = All articles needing copy edit }} }}<noinclude> <!-- Add categories and interwikis to the /doc subpage, not here! --> {{documentation}} </noinclude> ec3k9veij6hunm8idbswmqlu68diipq 366132 110637 2026-08-09T14:43:47Z en>Newbzy 0 Undid revision [[Special:Diff/1364403851|1364403851]] by [[Special:Contributions/Newbzy|Newbzy]] ([[User talk:Newbzy|talk]]) contested 366132 wikitext text/x-wiki {{SAFESUBST:<noinclude />#invoke:Unsubst||date=__DATE__ |$B= {{ambox | name = Copy edit | type = style | class = ambox-Copy_edit | image = [[File:Acap.svg|class=skin-invert|32px|alt=|link=]] | issue = This {{{1|article}}} '''may require [[Wikipedia:Basic copyediting|copy editing]] for {{{for|{{{reason|spelling, grammar, style, cohesion, or tone}}}}}}'''. | fix = You can assist by [[Special:EditPage/{{FULLPAGENAME}}|editing it]]. | removalnotice = yes | cat = Wikipedia articles needing copy edit | date = {{{date|}}} | all = All Wikipedia articles needing copy edit }} }}<noinclude> <!-- Add categories to the /doc subpage, not here! --> {{Documentation}} </noinclude> 2kb47gz79mjfxfzuivctcpjrv7mwwmd 366133 366106 2026-08-21T03:52:54Z ChiK 1136 ೧ revisions imported from [[:en:Template:Copy_edit]] 366106 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{ambox | subst = <includeonly>{{subst:substcheck}}</includeonly> | name = ನಕಲ್ ಮಾಂತುದ್ ಸಂಪೊಲಿಪು | type = style | class = ambox-Copy_edit | image = [[Image:Acap.svg|40x40px|alt=|link=]] | issue = This {{{1|article}}} '''may require [[:en:Wikipedia:Basic copyediting|copy editing]] for {{{for|{{{reason|grammar, style, cohesion, tone, or spelling}}}}}}'''. | fix = You can assist by [{{SERVER}}{{localurl:{{NAMESPACE}}:{{PAGENAME}}|action=edit}} editing it]. | removalnotice = yes | cat = Wikipedia articles needing copy edit | date = {{{date|}}} | all = All articles needing copy edit }} }}<noinclude> <!-- Add categories and interwikis to the /doc subpage, not here! --> {{documentation}} </noinclude> ec3k9veij6hunm8idbswmqlu68diipq 366140 366133 2026-08-21T04:07:05Z ChiK 1136 366140 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{ambox | subst = <includeonly>{{subst:substcheck}}</includeonly> | name = copy edit | type = style | class = ambox-Copy_edit | image = [[Image:Acap.svg|40x40px|alt=|link=]] | issue = ಈ {{{1|ಲೇಕನೊಡು}}} '''{{{for|{{{reason|ವ್ಯಾಕರಣೊ, ಬಾಸೆತ ಪೊರ್ಲು, ವಿಸಯೊನು ಒಗ್ಗುನ ಅತ್ತಂದೆ ಕಾಗುಣಿತೊ}}}}}} ಸರಿ ಮಲ್ತ್‌ದ್ [[:en:Wikipedia:Basic copyediting|ಬರವು ಸುದಾರನೆದ]] ಅಗತ್ಯೊಲಾ ಉಂಡು'''. | fix = ಈರ್ ಉಂದೆನ್ [{{SERVER}}{{localurl:{{NAMESPACE}}:{{PAGENAME}}|action=edit}} ಸಂಪೊಲಿತ್‌ದ್] ಸಕಾಯೊ ಮಲ್ಪೊಲಿ. | removalnotice = yes | cat = Wikipedia articles needing copy edit | date = {{{date|}}} | all = All articles needing copy edit }} }}<noinclude> <!-- Add categories and interwikis to the /doc subpage, not here! --> {{documentation}} </noinclude> 5hlsu34tjw41fm9zox8x038zbdtpoq2 366141 366140 2026-08-21T04:09:36Z ChiK 1136 366141 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{ambox | subst = <includeonly>{{subst:substcheck}}</includeonly> | name = copy edit | type = style | class = ambox-Copy_edit | image = [[Image:Acap.svg|40x40px|alt=|link=]] | issue = ಈ {{{1|ಲೇಕನೊಡು}}} '''{{{for|{{{reason|ವ್ಯಾಕರಣೊ, ಬಾಸೆತ ಪೊರ್ಲು, ವಿಸಯೊನು ಒಗ್ಗುನ ಅತ್ತಂದೆ ಕಾಗುಣಿತೊ}}}}}} ಸರಿ ಮಲ್ತ್‌ದ್ [[:en:Wikipedia:Basic copyediting|ಬರವು ಸುದಾರನೆದ]] ಅಗತ್ಯೊಲಾ ಉಂಡು'''. | fix = ಈರ್ ಉಂದೆನ್ [{{SERVER}}{{localurl:{{NAMESPACE}}:{{PAGENAME}}|action=edit}} ಸಂಪೊಲಿತ್‌ದ್] ಸಕಾಯೊ ಮಲ್ಪೊಲಿ. | removalnotice = yes | cat = ವಿಕಿಪೀಡಿಯ ಲೇಕನೊದ ಬರವು ಸುದಾರನೆ ಆವೊಡು | date = {{{date|}}} | all = ಮಾತಾ ಲೇಕನೊದ ಬರವು ಸುದಾರನೆ ಆವೊಡು }} }}<noinclude> <!-- Add categories and interwikis to the /doc subpage, not here! --> {{documentation}} </noinclude> 4ryzd3cgv09dwydyysj67ulblhnkn50 ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ (ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು) 4 6742 366092 365673 2026-08-20T22:52:27Z MABot 170 Bot: Archiving 1 thread (30 ದಿನೊಕುಲು ಕ್ಕಿಂತ ಹಳೆಯ) to [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ/ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು/ಪತ್ರಾಗಾರೊ/2026]] 366092 wikitext text/x-wiki {{ಚಾವಡಿ ಪುಟೊ header|ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು|alpha=yes| [[ತುಳು ಬಾಸೆ|ತುಳು]] ವಿಕಿಪೀಡಿಯೊದ ತಾಂತ್ರಿಕ ಸುದ್ದಿಲೆನ ಪುಟೊ. |ವಿಪಿ:ಚಾ|ವಿಪಿ:ಚಾತಾಸು}} {{Archive box| {{Search box|root=ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ (ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು)|search-width=20|search-button-label=ಪತ್ರಾಗಾರೊಡ್ ನಾಡ್ಲೆ|search-break=yes}} *[[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ/ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು ಪತ್ರಾಗಾರೊ|ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು ಪುಟೊತ ಪತ್ರಾಗಾರೊ]] }} __NEWSECTIONLINK__ __TOC__<div id="below_toc"></div> [[Category:ವಿಕಿಪೀಡಿಯ ಚಾವಡಿ]] [[Category:ವಿಕಿಪೀಡಿಯ ಲೇಸ್‍ಲು]] {{ಬಳಕೆದಾರೆ:MABot/config |archive = ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ/ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು/ಪತ್ರಾಗಾರೊ/%(year)s |algo = old(30d) |counter = 1 |archiveheader = |minthreadstoarchive = 1 |minthreadsleft = 2 |key = 5ba48e49ae85ef53ec35cbf6eec6ff38 }}</noinclude> {{clear}} == <span lang="en" dir="ltr">Tech News: 2026-31</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W31"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/31|Translations]] are available. '''Updates for editors''' * [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Wishlist item]] [[mw:Special:MyLanguage/ContentTranslation|Content Translation]] now supports dark mode, fulfilling a [[m:Community Wishlist/W544|Community Wishlist request]]. This brings the tool in line with the accessibility features available in the Vector 2022 and Minerva skins, helping reduce visual fatigue for users translating content. [https://phabricator.wikimedia.org/T367077] * DiscussionTools' source mode and the 2017 wikitext editor will now offer autocomplete for links (<bdi lang="zxx" dir="ltr"><code><nowiki>[[</nowiki></code></bdi>), templates (<bdi lang="zxx" dir="ltr"><code><nowiki>{{</nowiki></code></bdi>), HTML and parser tags (<bdi lang="zxx" dir="ltr"><code><nowiki><</nowiki></code></bdi>), and magic words (<bdi lang="zxx" dir="ltr"><code><nowiki>__</nowiki></code></bdi>), making it quicker and easier to insert links, templates, and other wiki markup while editing. [https://phabricator.wikimedia.org/T432400] * The [[mw:Special:MyLanguage/Readers/Reader Growth/Mobile page previews|Readers Growth team]] has concluded its experiment with mobile page previews and will not roll out the feature. Page Previews are a pop-up bottom sheet that appears when readers tap a blue link, showing a thumbnail, lead paragraph, and an option to open the article. The experiment showed flat retention and negative indicator metrics, suggesting that mobile web readers preferred navigating directly to linked articles rather than using page previews. * The Reader Experience team has seen encouraging early results from the [[mw:Special:MyLanguage/Readers/Reader Experience/Reading lists|Reading Lists feature]], with 93% of participating users reporting that it was useful. Reading Lists help active readers save articles for future reading and support their learning goals on Wikimedia projects. The team plans further improvements before expanding the feature to more users. * The [[mw:Special:MyLanguage/Wikimedia Apps/Team/Explore Feed Refresh|Explore Feed Refresh]] initiative was tested with new and casual Wikipedia app readers. The refreshed feed helps readers discover new and relevant content. After a 10.5% increase in engagement with the feed, Wikimedia Apps team has decided to scale the Home Feed redesign to iOS with the learnings from the Android release applied. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:23}} community-submitted {{PLURAL:23|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where subject names in the Article Guidance feature were displayed with incorrect capitalization on French Wikipedia, has now been fixed. Subject names will now follow the correct capitalization rules for the language. [https://phabricator.wikimedia.org/T427201] '''Updates for technical contributors''' * After running several [[mw:Special:MyLanguage/Contributors/Account Creation Experiments|Account Creation Experiments]] to improve registration completion rates, a new version of the username field on [[Special:CreateAccount|Create Account]] has been rolled out. It includes [[:c:File:Create account - July 2026 updates.png|a popover summarizing the username policy]] to provide clearer guidance during account creation. As part of this change, the messages <bdi lang="zxx" dir="ltr"><code><nowiki>createacct-helpusername</nowiki></code></bdi> and <bdi lang="zxx" dir="ltr"><code><nowiki>createacct-username-help</nowiki></code></bdi> that several communities have configured will no longer be used. If communities want to customize the guidance shown in the new popover, they can instead edit the following messages: <bdi lang="zxx" dir="ltr"><code><nowiki>createacct-username-policy-popover-bullet1</nowiki></code></bdi>, <bdi lang="zxx" dir="ltr"><code><nowiki>createacct-username-policy-popover-bullet2</nowiki></code></bdi>, and <bdi lang="zxx" dir="ltr"><code><nowiki>createacct-username-policy-popover-bullet3</nowiki></code></bdi>. [https://phabricator.wikimedia.org/T430604] * Later this week, the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|CodeMirror syntax highlighter]] will offer [[w:en:Theme (computing)|themes]]. The themes can be picked from a dropdown menu in the full [[mw:Special:MyLanguage/Help:Extension:CodeMirror#CodeMirror preferences|CodeMirror preferences]] dialog. For wikitext, available themes are default, colorblind-friendly (previously the colorblind preference option on [[Special:Preferences#mw-prefsection-editing]]) and no-highlighting. For code languages (i.e., CSS/JavaScript/JSON/Vue/Lua), there are several themes available. These same themes will eventually be available for wikitext, too. [https://phabricator.wikimedia.org/T163533] * From now on, wikis can restrict editing in the "User" namespace to only the page owner and certain user groups. [[mw:Special:MyLanguage/Manual:$wgRestrictUserPageEditing|Read the configuration documentation]] to learn more. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.14|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/31|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W31"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೦:೧೯, ೨೮ ಜುಲಾಯಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30856308 --> == <span lang="en" dir="ltr">Tech News: 2026-32</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W32"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/32|Translations]] are available. '''Updates for editors''' * The [[mw:Special:MyLanguage/Readers/Reader Experience|Reader Experience team]] has developed a [https://82db7c8d4b.catalyst.wmcloud.org/w/index.php?title=Regent%27s_Park&uselang=de patch demo] that wraps the page toolbar onto two lines when there is not enough horizontal space for all the buttons. This aims to reduce crowding in the Vector 2022 toolbar, which can occur on some language Wikipedias at certain screen widths. [https://phabricator.wikimedia.org/T429518] * The Reader Experience team is planning to launch [[mw:Special:MyLanguage/Readers/Reader Experience/Reading lists|Reading Lists]], a [[m:Special:MyLanguage/Community Wishlist Survey 2021/Mobile and apps/Have Apps reading lists available on Destop/Mobile|Community Wishlist item]], which is currently available to try in beta, as a full feature in September. Before then, volunteer translator help is needed for [https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-readinglists&filter=&action=translate string translations] into a number of languages. The feature supports reading and learning goals on Wikipedia. * Next week, the table of contents on Wikimedia Commons file pages will be improved by consolidating the file page table of contents with the page table of contents. This will make it easier to understand a file page’s structure, navigate to specific sections, and share links to individual sections. [https://phabricator.wikimedia.org/T332644] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:24}} community-submitted {{PLURAL:24|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where some [[w:TIFF|TIFF]] images failed to load after clicking their thumbnail, causing a broken image to be displayed instead of the full-size image, has now been fixed. [https://phabricator.wikimedia.org/T429326] '''Updates for technical contributors''' * The variable and function selector in AbuseFilter has been updated to support search and autocomplete. It will allow filter maintainers to find the desired variable or function more quickly. [https://phabricator.wikimedia.org/T323698] * The MJPEG and VP8 formats are removed from the video player. The MP4 format (MPEG-4 Part 2) is added instead, which provides higher quality videos to older iPhone devices. It may take a few weeks to retroactively update all existing videos. The default format for modern devices stays the same (VP9/WebM). [https://phabricator.wikimedia.org/T358266] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.15|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/32|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W32"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೧:೧೬, ೪ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30872536 --> == <span lang="en" dir="ltr">Tech News: 2026-33</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W33"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/33|Translations]] are available. '''Updates for editors''' * [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Wishlist item]] A new ChartWizard is [[c:Special:ChartWizard/Data:Example.Pie.chart|now available on Wikimedia Commons]] for users interested in creating charts from their own data. The wizard makes the [[mw:Special:MyLanguage/Extension:Chart|Chart extension]] more beginner-friendly by allowing editors to create charts, such as bar and pie charts, without needing to use JSON. Users can still switch to the JSON editor if they prefer. Feedback on the new tool is welcome on the [[m:Talk:Community Wishlist/W414|wish talk page]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:19}} community-submitted {{PLURAL:19|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where the Wikipedia iOS app’s Picture of the Day widget displayed the same image every day instead of updating daily, has now been fixed. [https://phabricator.wikimedia.org/T430692] '''Updates for technical contributors''' * [[mw:Special:MyLanguage/Extension:Math|Math formula]] SVG images will soon be generated in the browser instead of on the server. MathML continues to be generated on the server and renders in the browser without JavaScript. Wikibooks will see this change on 12 August, Wikisource on 19 August and Wikipedia from 20-27 August. You can try this by selecting "{{int:Mw-math-mathjax}}" in your preferences. This change is part of [[mw:Special:MyLanguage/RESTBase/deprecation|deprecating RESTBase]] and [[phab:T431372|deprecating Mathoid]]. [https://phabricator.wikimedia.org/T271001] * Category pages will soon support sorting entries by the time they are added to a category. This will make it easier to find recently or long-standing categorized pages. It will also improve workflows for maintenance categories such as deletion backlogs and other time-based review tasks. You can use <bdi lang="zxx" dir="ltr"><code><nowiki>cldsort=timestamp</nowiki></code></bdi> URL argument in category view to sort the entries. [https://phabricator.wikimedia.org/T433768] * [[mw:Special:MyLanguage/Extension:Gadgets|Gadgets]] and user scripts on Wikimedia wikis may now use [[phab:T395347|ES2018 features]] and [[phab:T419142|ES2019 features]] in JavaScript code. Previously, the platform only allowed up to ES2017. MediaWiki validates the source code to protect functionality from syntax errors and to ensure scripts are valid in all [[mw:Special:MyLanguage/Compatibility#Browser_support_matrix|supported browsers]]. [https://phabricator.wikimedia.org/T419142] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.16|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/33|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W33"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೨:೧೫, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30901051 --> == <span lang="en" dir="ltr">Tech News: 2026-34</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W34"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/34|Translations]] are available. '''Weekly highlight''' * The [[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Worklist|Worklist feature]] for the Event Registration tool is now live on all Wikimedia wikis. With Worklist, event organizers can add the articles their event will focus on directly to the event page. The Worklist also powers [[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Worklist#How Event Pathways uses the worklist|Event Pathways]] which notifies other editors of the upcoming or ongoing event when they edit an article featured in the event's Worklist. This is the minimum viable version (MVP), and feedback is welcome. Organizers are encouraged to try the feature. A hands-on [[m:Special:MyLanguage/Event:Worklist Setup Workshop: Get Your Event Ready|Worklist Setup Workshop]] will take place on 18 August at 16:00 UTC and 19 August at 11:00 UTC. '''Updates for editors''' * [[Special:ShortPages]] displays short pages by their size, but in many cases it gets filled with disambiguations and soft redirects, making it harder to find the short articles themselves. Starting this weekend, you will be able to choose not to include an article in the special page by adding the magic word <bdi lang="zxx" dir="ltr"><code><nowiki>__EXPECTSHORTPAGE__</nowiki></code></bdi>. [https://phabricator.wikimedia.org/T433203] * One new wiki has been created: a {{int:project-localized-name-group-wikipedia/en}} in [[d:Q3436680|Bole]] ([[w:bol:|<bdi lang="zxx" dir="ltr"><code><nowiki>w:bol:</nowiki></code></bdi>]]) [https://phabricator.wikimedia.org/T429921] * Starting the week of August 17, the page toolbar will wrap onto two lines when there is not enough horizontal space for all the buttons. This is a [[phab:T429518|fully merged patch from the Reader Experience team]] which aims to reduce crowding in the Vector 2022 toolbar, that may occur on some language Wikipedias at certain screen widths. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:16}} community-submitted {{PLURAL:16|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, uploading large files to Wikimedia Commons has become more stable and less prone to failure following some fixes related to the “Could not acquire lock” upload error. [https://phabricator.wikimedia.org/T386640] '''Updates for technical contributors''' * Debian Bullseye will reach the end of its Long Term Support on 31 August 2026. [[phab:T434103|Some Cloud VPS projects]] still have instances running Debian Bullseye. Maintainers of those projects are encouraged to migrate to Debian Bookworm or Debian Trixie. A [[wikitech:Help:Cloud VPS instance operating system migration|migration guide]] is available to help with the process, and users may also want to consider whether their workload is better suited to Toolforge. If you need help or cannot complete the migration by 31 August, please contact the Cloud VPS admins as soon as possible. [[listarchive:list/cloud-announce@lists.wikimedia.org/thread/RVIPQSYLKMSL5M46JP6NEJVGVE6I2RXQ/|Read more]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.16|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/34|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W34"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೨:೩೩, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30922806 --> j6kf52w6vdk1jr7sfpiase7ewdb0oq0 ಟೆಂಪ್ಲೇಟ್:Redirect category shell 10 7317 366193 365739 2026-08-20T17:28:15Z en>Jonesey95 0 magic word has been implemented as of today 366193 wikitext text/x-wiki __NONEWSECTIONLINK__{{Mbox | name = Redirect category shell | type = move | image = none | style = margin-top: 1.1em; border: solid 1px darkblue; border-left-width: 0.5em; | textstyle = padding-top: 0.9em; padding-bottom: 0.9em; | text = '''This {{Talk other|talk page|page}} is a [[Wikipedia:Redirect|redirect]]. <small>The following [[Wikipedia:Categorizing redirects|categories]] are used to track and monitor this redirect:</small>'''{{#if:{{{h|}}} |{{block indent|1= ''{{{h}}}''}} }} <!-- Automatically detect protected redirects: --><includeonly>{{#switch: {{PROTECTIONLEVEL:move}} |sysop|templateeditor|extendedconfirmed={{pp-move|small=yes|catonly=no}} }}{{#switch: {{PROTECTIONLEVEL:edit}} |sysop |templateeditor |extendedconfirmed |autoconfirmed={{pp-protected|small=yes}}{{R protected}} | <!--Not protected, or only semi-move-protected--> }}</includeonly>{{#if: {{{1|}}}||<includeonly> * {{red|'''Important – Please Read! {{maroon|This template should {{em|not}} be applied without parameters by bot nor by any automated or semi-automated process. It should {{em|not}} be used without parameters {{em|unless you want to learn how to categorize redirects}}. For editors who want to learn how to categorize redirects, this template is a {{em|learning tool}}. {{em|Only}} those editors who intend to return to the redirect to learn which rcats to use should apply this template without parameters, or with an empty first parameter!}}'''}}</includeonly> * '''Manifold sort''': If help is needed to determine appropriate categories, then this redirect populates '''{{Cat|Miscellaneous redirects}}'''. Monitors of that category will check this redirect and add or remove [[Wikipedia:Categorizing redirects|rcats]] as needed.{{#ifeq: {{lc:{{{nocat|false}}}}} | false |{{{category|<includeonly>[[Category:Miscellaneous redirects]]</includeonly>}}}}} }} {{#if: {{{2|}}}|{{{2}}}|{{{1|}}}}}{{#if: {{{1|}}}{{{2|}}}||{{#ifeq: {{ROOTPAGENAME}}|Redirect category shell||[[Category:Redirect category shell without parameters]]}}}} ''<small>When appropriate, [[Wikipedia:Protection policy|protection levels]] are automatically sensed, described and categorized.</small>'' }}<includeonly>__EXPECTSHORTPAGE__<!--To prevent pages from appearing on Special:ShortPages. See Tech News 2026-34. --></includeonly><noinclude> {{Documentation}} </noinclude> dt89h4apbs7l1rhl2q3letiqcxmyceg 366194 366193 2026-08-21T10:15:05Z ChiK 1136 ೧ revisions imported from [[:en:Template:Redirect_category_shell]] 366193 wikitext text/x-wiki __NONEWSECTIONLINK__{{Mbox | name = Redirect category shell | type = move | image = none | style = margin-top: 1.1em; border: solid 1px darkblue; border-left-width: 0.5em; | textstyle = padding-top: 0.9em; padding-bottom: 0.9em; | text = '''This {{Talk other|talk page|page}} is a [[Wikipedia:Redirect|redirect]]. <small>The following [[Wikipedia:Categorizing redirects|categories]] are used to track and monitor this redirect:</small>'''{{#if:{{{h|}}} |{{block indent|1= ''{{{h}}}''}} }} <!-- Automatically detect protected redirects: --><includeonly>{{#switch: {{PROTECTIONLEVEL:move}} |sysop|templateeditor|extendedconfirmed={{pp-move|small=yes|catonly=no}} }}{{#switch: {{PROTECTIONLEVEL:edit}} |sysop |templateeditor |extendedconfirmed |autoconfirmed={{pp-protected|small=yes}}{{R protected}} | <!--Not protected, or only semi-move-protected--> }}</includeonly>{{#if: {{{1|}}}||<includeonly> * {{red|'''Important – Please Read! {{maroon|This template should {{em|not}} be applied without parameters by bot nor by any automated or semi-automated process. It should {{em|not}} be used without parameters {{em|unless you want to learn how to categorize redirects}}. For editors who want to learn how to categorize redirects, this template is a {{em|learning tool}}. {{em|Only}} those editors who intend to return to the redirect to learn which rcats to use should apply this template without parameters, or with an empty first parameter!}}'''}}</includeonly> * '''Manifold sort''': If help is needed to determine appropriate categories, then this redirect populates '''{{Cat|Miscellaneous redirects}}'''. Monitors of that category will check this redirect and add or remove [[Wikipedia:Categorizing redirects|rcats]] as needed.{{#ifeq: {{lc:{{{nocat|false}}}}} | false |{{{category|<includeonly>[[Category:Miscellaneous redirects]]</includeonly>}}}}} }} {{#if: {{{2|}}}|{{{2}}}|{{{1|}}}}}{{#if: {{{1|}}}{{{2|}}}||{{#ifeq: {{ROOTPAGENAME}}|Redirect category shell||[[Category:Redirect category shell without parameters]]}}}} ''<small>When appropriate, [[Wikipedia:Protection policy|protection levels]] are automatically sensed, described and categorized.</small>'' }}<includeonly>__EXPECTSHORTPAGE__<!--To prevent pages from appearing on Special:ShortPages. See Tech News 2026-34. --></includeonly><noinclude> {{Documentation}} </noinclude> dt89h4apbs7l1rhl2q3letiqcxmyceg ಟೆಂಪ್ಲೇಟ್:Flatlist 10 7437 366111 219095 2026-08-20T02:43:39Z en>Wbm1058 0 notify protected page of requested move 366111 wikitext text/x-wiki <noinclude>{{Requested move notice|1=Template:Flat list|2=Template talk:Hlist#Requested move 3 August 2026}} </noinclude><templatestyles src="Hlist/styles.css"/><div class="hlist {{{class|}}}" {{#if:{{{style|}}}{{{indent|}}}|style="{{#if:{{{indent|}}}|margin-left: {{#expr:{{{indent}}}*1.6}}em;}} {{{style|}}}"}}>{{#if:{{{1|}}}| {{{1}}} </div>}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using flatlist with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Flatlist]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | class | indent | style }}<noinclude></div><!-- Needed to prevent Linter errors here; when Flatlist without parameters is used to start a list, Endflatlist provides the closing /div tag. --> {{documentation}} </noinclude> ahdzwtj48lbvqiseivd08z3ozk637ls 366112 366111 2026-08-21T02:53:26Z ChiK 1136 ೧ revisions imported from [[:en:Template:Flatlist]] 366111 wikitext text/x-wiki <noinclude>{{Requested move notice|1=Template:Flat list|2=Template talk:Hlist#Requested move 3 August 2026}} </noinclude><templatestyles src="Hlist/styles.css"/><div class="hlist {{{class|}}}" {{#if:{{{style|}}}{{{indent|}}}|style="{{#if:{{{indent|}}}|margin-left: {{#expr:{{{indent}}}*1.6}}em;}} {{{style|}}}"}}>{{#if:{{{1|}}}| {{{1}}} </div>}}{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using flatlist with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Flatlist]] with unknown parameter "_VALUE_"|ignoreblank=y| 1 | class | indent | style }}<noinclude></div><!-- Needed to prevent Linter errors here; when Flatlist without parameters is used to start a list, Endflatlist provides the closing /div tag. --> {{documentation}} </noinclude> ahdzwtj48lbvqiseivd08z3ozk637ls ಟೆಂಪ್ಲೇಟ್:Copy edit/doc 10 8419 366136 110707 2026-07-24T08:47:14Z en>Mathglot 0 /* top */ Add {{Newcomer task notice}}. 366136 wikitext text/x-wiki {{Documentation subpage}} {{Redirect|Template:Spacing|inline spacing templates|Template:Inline spacing templates}} {{High-use}} <!-- Add categories where indicated at the bottom of this page and interwikis at Wikidata --> {{Twinkle standard installation}} {{Newcomer task notice|copyedit}} {{Never substitute}} {{tsh|c/e}} == Usage == This template tags an article for [[Wikipedia:Basic copyediting|copy editing]] and should be added at the top of the article page, as follows: :{{tnull|Copy edit|for=|date{{=}}{{CURRENTMONTHNAME}} {{CURRENTYEAR}}}} This template will auto-categorize tagged articles into [[:Category:Wikipedia articles needing copy edit]], or a dated subcategory thereof. Do not <code>[[Wikipedia:Substitution|subst:]]</code> this template. This [[Wikipedia:Template messages|template]] is a [[Wikipedia:Manual of Style/Self-references to avoid|self-reference]]. Do not add this template to drafts. === Optional parameters === * This template takes an optional parameter that replaces the word "article" in the displayed message with arbitrary text. For example, {{tnull|Copy edit|section}} displays "This section needs [[Wikipedia:Basic copyediting|copy editing]]." * This template takes the optional parameter {{para|for}} (a.k.a. {{para|reason}}) which can be used to explain what issues need to be addressed, e.g. {{para|for|consistent spelling}}. The default value is "grammar, style, cohesion, tone, or spelling". * This template takes the optional parameter {{para|date}}, with the format {{para|date|{{CURRENTMONTHNAME}} {{CURRENTYEAR}}}}. A bot will automatically fill the parameter if it is initially left empty. == TemplateData == {{TemplateData header}} <templatedata> { "description": "Use this cleanup template to tag an article for copy editing, such as for grammar, style, cohesion, tone or spelling.", "format": "inline", "params": { "1": { "label": "Affected area", "type": "string", "autovalue": "section", "suggestedvalues": [ "section" ], "default": "article" }, "date": { "label": "Month and year", "description": "The month and year that the template was placed (in full). \"{{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}\" inserts the current month and year automatically.", "type": "line", "autovalue": "{{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}", "example": "January 2013", "suggested": true }, "for": { "aliases": [ "reason" ], "label": "Reason", "description": "Text to replace \"grammar, style, cohesion, tone, or spelling\" in the template", "type": "string" } }, "paramOrder": [ "date", "1", "for" ] } </templatedata> == Tracking categories == * {{clc|All Wikipedia articles needing copy edit}} == Other templates that add pages to {{cl|Wikipedia articles needing copy edit}} == * {{Tl|Copy edit inline}} * {{Tl|Copy edit section}} * {{tl|Inappropriate person}} == Redirects == {{columns-list|colwidth=15em| #{{Tl|Ced}} #{{Tl|Cleanup-copyedit}} #{{Tl|Cleanup-english}} #{{Tl|Copy-edit}} #{{tl|Copyedit}} #{{Tl|Copyediting}} #{{Tl|Gcheck}} #{{Tl|Grammar}} #{{tl|Grammar check}} #{{Tl|Spacing}} #{{tl|Spelling}} #{{tl|Repetition}} #{{tl|Verbosity}} }} == See also == * {{TL|Cleanup tense}} * {{Tl|Tone}} * {{Tl|Verify spelling}} * [[Wikipedia:Template index/Cleanup]] * [[Wikipedia:WikiProject Guild of Copy Editors]] <includeonly>{{sandbox other|| <!-- Categories below this line; interwikis at Wikidata --> [[Category:Cleanup templates]] }}</includeonly> axj435q95epm77ohrhsx8ryy7dy61ox 366137 366136 2026-08-21T03:52:55Z ChiK 1136 ೧ revisions imported from [[:en:Template:Copy_edit/doc]] 366136 wikitext text/x-wiki {{Documentation subpage}} {{Redirect|Template:Spacing|inline spacing templates|Template:Inline spacing templates}} {{High-use}} <!-- Add categories where indicated at the bottom of this page and interwikis at Wikidata --> {{Twinkle standard installation}} {{Newcomer task notice|copyedit}} {{Never substitute}} {{tsh|c/e}} == Usage == This template tags an article for [[Wikipedia:Basic copyediting|copy editing]] and should be added at the top of the article page, as follows: :{{tnull|Copy edit|for=|date{{=}}{{CURRENTMONTHNAME}} {{CURRENTYEAR}}}} This template will auto-categorize tagged articles into [[:Category:Wikipedia articles needing copy edit]], or a dated subcategory thereof. Do not <code>[[Wikipedia:Substitution|subst:]]</code> this template. This [[Wikipedia:Template messages|template]] is a [[Wikipedia:Manual of Style/Self-references to avoid|self-reference]]. Do not add this template to drafts. === Optional parameters === * This template takes an optional parameter that replaces the word "article" in the displayed message with arbitrary text. For example, {{tnull|Copy edit|section}} displays "This section needs [[Wikipedia:Basic copyediting|copy editing]]." * This template takes the optional parameter {{para|for}} (a.k.a. {{para|reason}}) which can be used to explain what issues need to be addressed, e.g. {{para|for|consistent spelling}}. The default value is "grammar, style, cohesion, tone, or spelling". * This template takes the optional parameter {{para|date}}, with the format {{para|date|{{CURRENTMONTHNAME}} {{CURRENTYEAR}}}}. A bot will automatically fill the parameter if it is initially left empty. == TemplateData == {{TemplateData header}} <templatedata> { "description": "Use this cleanup template to tag an article for copy editing, such as for grammar, style, cohesion, tone or spelling.", "format": "inline", "params": { "1": { "label": "Affected area", "type": "string", "autovalue": "section", "suggestedvalues": [ "section" ], "default": "article" }, "date": { "label": "Month and year", "description": "The month and year that the template was placed (in full). \"{{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}\" inserts the current month and year automatically.", "type": "line", "autovalue": "{{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}}", "example": "January 2013", "suggested": true }, "for": { "aliases": [ "reason" ], "label": "Reason", "description": "Text to replace \"grammar, style, cohesion, tone, or spelling\" in the template", "type": "string" } }, "paramOrder": [ "date", "1", "for" ] } </templatedata> == Tracking categories == * {{clc|All Wikipedia articles needing copy edit}} == Other templates that add pages to {{cl|Wikipedia articles needing copy edit}} == * {{Tl|Copy edit inline}} * {{Tl|Copy edit section}} * {{tl|Inappropriate person}} == Redirects == {{columns-list|colwidth=15em| #{{Tl|Ced}} #{{Tl|Cleanup-copyedit}} #{{Tl|Cleanup-english}} #{{Tl|Copy-edit}} #{{tl|Copyedit}} #{{Tl|Copyediting}} #{{Tl|Gcheck}} #{{Tl|Grammar}} #{{tl|Grammar check}} #{{Tl|Spacing}} #{{tl|Spelling}} #{{tl|Repetition}} #{{tl|Verbosity}} }} == See also == * {{TL|Cleanup tense}} * {{Tl|Tone}} * {{Tl|Verify spelling}} * [[Wikipedia:Template index/Cleanup]] * [[Wikipedia:WikiProject Guild of Copy Editors]] <includeonly>{{sandbox other|| <!-- Categories below this line; interwikis at Wikidata --> [[Category:Cleanup templates]] }}</includeonly> axj435q95epm77ohrhsx8ryy7dy61ox ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK 3 9575 366142 365210 2026-08-21T05:43:14Z Kavitha G. Kana 1040 /* ಈ ಲೇಕನೊಡು ಅರ್ತ ಆವಂದಿ ವಾಕ್ಯೊಲು ಸರಿ ಮಲ್ತ್‌ದ್ */ಪೊಸ ವಿಭಾಗ 366142 wikitext text/x-wiki {{Template:Welcome|realName=|name=ChidanandaKampa}} == [[ಚಂದ್ರೆ]] == Hello, Is [https://tcy.wikipedia.org/w/index.php?title=%E0%B2%9A%E0%B2%82%E0%B2%A6%E0%B3%8D%E0%B2%B0%E0%B3%86&type=revision&diff=143298&oldid=143297 this] correct? Thanks, [[ಬಳಕೆದಾರೆ:Kwamikagami|Kwamikagami]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kwamikagami|ಪಾತೆರ್ಲೆ]]) ೧೯:೫೭, ೩೧ ಜನವರಿ ೨೦೨೨ (IST) == WikiConference India 2023: Program submissions and Scholarships form are now open == Dear Wikimedian, We are really glad to inform you that '''[[:m:WikiConference India 2023|WikiConference India 2023]]''' has been successfully funded and it will take place from 3 to 5 March 2023. The theme of the conference will be '''Strengthening the Bonds'''. We also have exciting updates about the Program and Scholarships. The applications for scholarships and program submissions are already open! You can find the form for scholarship '''[[:m:WikiConference India 2023/Scholarships|here]]''' and for program you can go '''[[:m:WikiConference India 2023/Program Submissions|here]]'''. For more information and regular updates please visit the Conference [[:m:WikiConference India 2023|Meta page]]. If you have something in mind you can write on [[:m:Talk:WikiConference India 2023|talk page]]. ‘‘‘Note’’’: Scholarship form and the Program submissions will be open from '''11 November 2022, 00:00 IST''' and the last date to submit is '''27 November 2022, 23:59 IST'''. Regards [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೧೬ ನವಂಬರ್ ೨೦೨೨ (IST) (on behalf of the WCI Organizing Committee) <!-- Message sent by User:Nitesh Gill@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2023_active_users,_scholarships_and_program&oldid=24082246 --> == WikiConference India 2023: Open Community Call and Extension of program and scholarship submissions deadline == Dear Wikimedian, Thank you for supporting Wiki Conference India 2023. We are humbled by the number of applications we have received and hope to learn more about the work that you all have been doing to take the movement forward. In order to offer flexibility, we have recently extended our deadline for the Program and Scholarships submission- you can find all the details on our [[:m:WikiConference India 2023|Meta Page]]. COT is working hard to ensure we bring together a conference that is truly meaningful and impactful for our movement and one that brings us all together. With an intent to be inclusive and transparent in our process, we are committed to organizing community sessions at regular intervals for sharing updates and to offer an opportunity to the community for engagement and review. Following the same, we are hosting the first Open Community Call on the 3rd of December, 2022. We wish to use this space to discuss the progress and answer any questions, concerns or clarifications, about the conference and the Program/Scholarships. Please add the following to your respective calendars and we look forward to seeing you on the call * '''WCI 2023 Open Community Call''' * '''Date''': 3rd December 2022 * '''Time''': 1800-1900 (IST) * '''Google Link'''': https://meet.google.com/cwa-bgwi-ryx Furthermore, we are pleased to share the email id of the conference contact@wikiconferenceindia.org which is where you could share any thoughts, inputs, suggestions, or questions and someone from the COT will reach out to you. Alternatively, leave us a message on the Conference [[:m:Talk:WikiConference India 2023|talk page]]. Regards [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೧:೫೧, ೨ ದಸಂಬರ್ ೨೦೨೨ (IST) On Behalf of, WCI 2023 Core organizing team. <!-- Message sent by User:Nitesh Gill@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2023_active_users,_scholarships_and_program&oldid=24083503 --> == Archiver bot == Hello. I was reviewing [[Special:Contribs/MABot|MABot]] logs and saw that [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ]] is now being archived at [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ ಪತ್ರಾಗಾರೊ]]. Is this intended? I'm asking as I'd rather avoid archiving in the wrong place. If this is now where you'd like to archive threads for that project, I can continue. Thanks in advance for your answer. Sincerely, [[ಬಳಕೆದಾರೆ:MarcoAurelio|MarcoAurelio]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MarcoAurelio|ಪಾತೆರ್ಲೆ]]) ೨೩:೨೫, ೮ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) :Thank you for your response this [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ ಪತ್ರಾಗಾರೊ]] is right place. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೨:೩೮, ೯ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) ::Thank you. I'll re-configure the bot. Sincerely, [[ಬಳಕೆದಾರೆ:MarcoAurelio|MarcoAurelio]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MarcoAurelio|ಪಾತೆರ್ಲೆ]]) ೦೫:೦೬, ೧೦ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) == Mahatma Gandhi 2023 edit-a-thon Barnstar Award == ''Sorry for writing this message in English - feel free to help us translating it'' {| style="background-color: #fdffe7; border: 1px solid #fceb92;" | rowspan="2" style="vertical-align: middle; padding: 5px;" |[[ಫೈಲ್:Special_Gold_Barnstar.png|100x100px]] | style="font-size: x-large; padding: 3px 3px 0 3px; height: 1.5em;" |'''Congratulations!!!''' |- | style="vertical-align: middle; padding: 3px;" |Thank you for contributing to the [[:meta:Karavali Wikimedians/Events/Mahatma Gandhi 2023 edit-a-thon|Mahatma Gandhi 2023 edit-a-thon]]. We are appreciative of your tireless efforts to create articles about Mahatma Gandhi as well as other Independence movements on your local Wikipedia. We are deeply inspired by your persistent efforts, dedication to improve articles. We are lucky to have amazing Wikimedian like you in our movement. Thank you for your contribution to Wikipedia. Hopefully, Wikipedia will continue to enrich your active constructive editing in the future. Thanks for your contribution <br/> '''Tamanpreet Kaur''' <br/> ''Punjabi Wikimedian''. [[ಬಳಕೆದಾರೆ:Tamanpreet Kaur|Tamanpreet Kaur]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Tamanpreet Kaur|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೧, ೧೯ ಅಕ್ಟೋಬರ್ ೨೦೨೩ (IST) |} == Organising Feminism and Folklore == [[File:Feminism and Folklore 2024 logo.svg | 350px | right]] Hello Community Organizers, Thank you for organising Feminism and Folklore writing competition on your wiki. We congratulate you in joining and celebrating our cultural heritage and promoting gender equality on Wikipedia. To encourage boost for the contributions of the participants, we're offering prizes for Feminism and Folklore local prizes. Each Wikipedia will have three local winners: *First Prize: $15 USD *Second Prize: $10 USD *Best Jury Article: $5 USD All this will be in '''gift voucher format only'''. Kindly inform your local community regarding these prizes and post them on the local project page The Best Jury Article will be chosen by the jury based on how unique the article is aligned with the theme. The jury will review all submissions and decide the winner together, making sure it's fair. These articles will also be featured on our social media handles. We're also providing internet and childcare support to the first 50 organizers and Jury members for who request for it. Remember, only 50 organizers will get this support, and it's given on a first-come, first-served basis. The registration form will close after 50 registrations, and the deadline is March 15, 2024. This support is optional and not compulsory, so if you're interested, fill out the form [https://docs.google.com/forms/d/e/1FAIpQLSdnytyact-HR6DvsWwnrVeWuzMfuNH1dSjpF24m6od-f3LzZQ/viewform here]. Each organizer/jury who gets support will receive $30 USD in gift voucher format, even if they're involved in more than one wiki. No dual support will be provided if you have signed up in more than one language. This support is meant to appreciate your volunteer support for the contest. We also invite all organizers and jury members to join us for office hours on '''Saturday, March 2, 2024'''. This session will help you understand the jury process for both contests and give you a chance to ask questions. More details are on [https://meta.wikimedia.org/wiki/Wiki_Loves_Folklore_2024_Office_Hour_2 meta page]. Let's celebrate our different cultures and work towards gender equality on Wikipedia! Best regards, Rockpeterson [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೧:೨೬, ೨೯ ಪೆಬ್ರವರಿ ೨೦೨೪ (IST) <!-- Message sent by User:Rockpeterson@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Rockpeterson/fnf2024golbal&oldid=26304232 --> == Translation request == Hello, ChiK. Can you translate and upload the articles [[:en:Azerbaijani manat]], [[:en:Azerbaijani manat sign]] and [[:en:Campi Flegrei]] in Tulu Wikipedia? Yours sincerely, [[ಬಳಕೆದಾರೆ:Oirattas|Oirattas]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Oirattas|ಪಾತೆರ್ಲೆ]]) ೨೧:೫೬, ೧೫ ಸಪ್ಟಂಬರ್ ೨೦೨೪ (IST) :Thank you very much for the [[ಅಜೆರ್ಬೈಜಾನಿ ಮನಾತ್ ಗುರ್ತ]] article. Can you also translate and upload the articles [[:en:Azerbaijani manat]] and [[:en:Campi Flegrei]] in Tulu Wikipedia (and also all these three articles in Kannada Wikipedia)? [[ಬಳಕೆದಾರೆ:Oirattas|Oirattas]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Oirattas|ಪಾತೆರ್ಲೆ]]) ೨೧:೧೧, ೧೨ ಅಕ್ಟೋಬರ್ ೨೦೨೪ (IST) == Organising Tuluvas Aati Month == [[File:Tuluvas Aati Month banner logo.svg| 150px | right]] Hello Organizers, We want to extend our heartfelt gratitude to you for making the '''[[m:Tuluvas Aati Month|Tuluvas Aati Month]]''' event a success. Your time, dedication, and expertise brought this cultural celebration to life, creating meaningful experiences for everyone involved. To help us improve future events and celebrate the efforts of everyone. Each organizer/jury who gets support will receive in gift card format. please fill out [https://docs.google.com/forms/d/e/1FAIpQLSd3RWB1kvSyR77kYaZtI-S1T9da48Cd7j6giMb6naSDXcYl9Q/viewform this form]. Best regards,<br> '''Tuluvas Aati Month Team'''. [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೮:೫೬, ೧ ನವಂಬರ್ ೨೦೨೪ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tuluvas_Aati_Month/JuryList&oldid=27685549 --> == Invitation to Participate in the Wikimedia SAARC Conference Community Engagement Survey == Dear Community Members, I hope this message finds you well. Please excuse the use of English; we encourage translations into your local languages to ensure inclusivity. We are conducting a Community Engagement Survey to assess the sentiments, needs, and interests of South Asian Wikimedia communities in organizing the inaugural Wikimedia SAARC Regional Conference, proposed to be held in Kathmandu, Nepal. This initiative aims to bring together participants from eight nations to collaborate towards shared goals. Your insights will play a vital role in shaping the event's focus, identifying priorities, and guiding the strategic planning for this landmark conference. Survey Link: https://forms.gle/en8qSuCvaSxQVD7K6 We kindly request you to dedicate a few moments to complete the survey. Your feedback will significantly contribute to ensuring this conference addresses the community's needs and aspirations. Deadline to Submit the Survey: 20 January 2025 Your participation is crucial in shaping the future of the Wikimedia SAARC community and fostering regional collaboration. Thank you for your time and valuable input. Warm regards,<br> [[:m:User:Biplab Anand|Biplab Anand]] <!-- Message sent by User:Biplab Anand@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Biplab_Anand/lists&oldid=28078122 --> == Thank you for being a medical contributors! == <div lang="en" dir="ltr" class="mw-content-ltr"> {| style="background-color: #fdffe7; border: 1px solid #fceb92;" |rowspan="2" style="vertical-align: middle; padding: 5px;" | [[File:Wiki Project Med Foundation logo.svg|130px]] |style="font-size: x-large; padding: 3px 3px 0 3px; height: 1.5em;" |'''The 2024 Cure Award''' |- | style="vertical-align: middle; padding: 3px;" |In 2024 you '''[[mdwiki:WikiProjectMed:WikiProject_Medicine/Stats/Top_medical_editors_2024_(all)|were one of the top medical editors in your language]]'''. Thank you from [[m:WikiProject_Med|Wiki Project Med]] for helping bring free, complete, accurate, up-to-date health information to the public. We really appreciate you and the vital work you do! Wiki Project Med Foundation is a [[meta:Wikimedia_thematic_organizations|thematic organization]] whose mission is to improve our health content. '''[[meta:Wiki_Project_Med#People_interested|Consider joining for 2025]]''', there are no associated costs. Additionally one of our primary efforts revolves around translating health content. We invite you to '''[https://mdwiki.toolforge.org/Translation_Dashboard/index.php try our new workflow]''' if you have not already. Our dashboard automatically [https://mdwiki.toolforge.org/Translation_Dashboard/leaderboard.php collects statistics] of your efforts and we are working on [https://mdwiki.toolforge.org/fixwikirefs.php tools to automatically improve formating]. |} Thanks again :-) -- [[mdwiki:User:Doc_James|<span style="color:#0000f1">'''Doc James'''</span>]] along with the rest of the team at '''[[m:WikiProject_Med|Wiki Project Med Foundation]]''' ೧೧:೫೪, ೨೬ ಜನವರಿ ೨೦೨೫ (IST) </div> <!-- Message sent by User:Doc James@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Top_Other_Language_Editors_2024&oldid=28172893 --> == Feminism and Folklore 2025: Important Updates for Organizers & Jury == Hello Community Organizers and Jury, Thank you for organising Feminism and Folklore writing competition on your wiki. Feminism and Folklore is the largest Wikipedia contest organized by community members. We congratulate you in joining and celebrating our cultural heritage and promoting gender equality on Wikipedia. To encourage boost for the contributions of the participants, we're offering prizes for Feminism and Folklore local prizes. Each Wikipedia will have three local winners: # First Prize: $25 USD # Second Prize: $20 USD # Best Jury Article: $15 USD All this will be in '''gift voucher format only'''. Prizes will only be given to users who have more than 5 accepted articles. No prizes will be given for users winning below 5 accepted articles. Kindly inform your local community regarding these prizes and post them on the local project page The Best Jury Article will be chosen by the jury based on how unique the article is aligned with the theme. The jury will review all submissions and decide the winner together, making sure it's fair. These articles will also be featured on our social media handles. We're also providing internet and childcare support to the first 75 organizers and Jury members for those who request for it. Remember, only 75 organizers will get this support, and it's given on a first-come, first-served basis. The registration form will close after 75 registrations, and the deadline is <nowiki>'''</nowiki>March 5, 2025<nowiki>'''</nowiki>. This support is optional and not compulsory, so if you're interested, fill out the [https://docs.google.com/forms/d/e/1FAIpQLSeum8md6FqHY1ISWRLW5bqOAv_lcd1tpVtMMZfWKRDU_IffLQ/viewform?usp=dialog Form] Each organizer/jury who gets support will receive $40 USD in gift voucher format, even if they're involved in more than one wiki. No dual support will be provided if you have signed up in more than one language. This support is meant to appreciate your volunteer support for the contest. We also invite all organizers and jury members to join us for Advocacy session on '''Saturday, Feb 28, 2025'''. This session will help you understand the jury process for both contests and give you a chance to ask questions. More details are on [[meta:Event:Telling untold stories: How to document gendered narratives in Folklore on Wikipedia|Event:Telling untold stories: How to document gendered narratives in Folklore on Wikipedia - Meta]] Let's celebrate our different cultures and work towards gender equality on Wikipedia! Best regards, Stella and Tiven Wiki loves folklore international team [[User:SAgbley|SAgbley]] ([[User talk:SAgbley|talk]]) 04:39, 25 February 2025 (UTC) <!-- Message sent by User:Joris Darlington Quarshie@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Joris_Darlington_Quarshie/Community_Prizes&oldid=28309519 --> == Final reminder that the deadline for submitting your Feminism and Folklore 2025 results == <div style="border:8px maroon ridge;padding:6px;> Hello {{PAGENAME}} [[File:Feminism and Folklore logo.svg | right | frameless]] I hope this message finds you well. This is a final reminder that the deadline for submitting your Feminism and Folklore 2025 results on Meta has elapsed. For participant prizes to be processed, your community must submit the necessary information as soon as possible. Please use the following link to submit your results: [https://meta.wikimedia.org/wiki/Feminism_and_Folklore_2025/Results Submit your results] We have extended the {{font color||yellow|'''submission deadline to 12 June 2025'''.}} Kindly note that failure to submit by this date will result in your group’s participants not receiving any prizes from us. Thank you for your attention to this important matter and for your continued contributions to the Feminism and Folklore campaign. Warm regards,<br> Stella<br> On behalf of Feminism and Folklore Team<br> [[User:MediaWiki message delivery|MediaWiki message delivery]] ([[User talk:MediaWiki message delivery|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 12:39, 08 June 2025 (UTC) </div> <!-- Message sent by User:Joris Darlington Quarshie@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Template:FnF_2025_reminder_list&oldid=28842648 --> == Notice of expiration of your interface-admin right == <div dir="ltr">Hi, as part of [[:m:Special:MyLanguage/Global reminder bot|Global reminder bot]], this is an automated reminder to let you know that your permission "interface-admin" (ಅಂತರಮುಕ ನಿರ್ವಾಹಕೆರ್) will expire on 2025-11-29 15:03:18. Please renew this right if you would like to continue using it. <i>In other languages: [[:m:Special:MyLanguage/Global reminder bot/Messages/default|click here]]</i> [[ಬಳಕೆದಾರೆ:Leaderbot|Leaderbot]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Leaderbot|ಪಾತೆರ್ಲೆ]]) ೦೧:೧೩, ೨೩ ನವಂಬರ್ ೨೦೨೫ (IST)</div> == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Feminism and Folklore 2026 starts soon == <div style="border:8px maroon ridge;padding:6px;"> [[File:Feminism and Folklore 2026 logo.svg|centre|550px|frameless]] ::<div lang="en" dir="ltr" class="mw-content-ltr"> <div style="text-align: center; width: 100%;">''{{int:please-translate}}''</div> ;Invitation to Organize Feminism and Folklore 2026 Dear Wikimedian, We thank you for taking an initiative to organize the '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]''' writing competition on your local Wikipedia. The international campaign will run from '''1 February to 31 March 2026''' and aims to improve coverage of feminism, women’s histories, gender-related topics, and folk culture across Wikipedia projects. ;About the Campaign '''Feminism and Folklore''' is a global writing initiative that complements the '''[[:c:Commons:Wiki Loves Folklore 2026|Wiki Loves Folklore]]''' photography competition. While Wiki Loves Folklore focuses on visual documentation, this writing campaign addresses the '''gender gap on Wikipedia''' by improving encyclopedic content related to folk culture and marginalized voices. ;What Can Participants Write About? Communities can contribute by creating, expanding, or translating articles related to: * Folk festivals, rituals, and celebrations * Folk dances, music, and traditional performances * Women and queer figures in folklore * Women in mythology and oral traditions * Women warriors, witches, and witch-hunting narratives * Fairy tales, folk stories, and legends * Folk games, sports, and cultural practices Participants may work from curated article lists or generate new article suggestions using campaign tools. ;Checklist for Organizers Organizers are requested to complete the following steps to register their community: # Create a local project page on your wiki [[:m:Feminism and Folklore/Sample|(see sample)]] # Set up the campaign using the '''CampWiz''' tool # Prepare a local article list and clearly mention: #* Campaign timeline #* Local and international prizes # Request a site notice from local administrators [[:mr:Template:SN-FNF|(see sample)]] # Add your local project page and CampWiz link to the '''[[:m:Feminism and Folklore 2026/Project Page|Meta project page]]''' ;Campaign Tools The Wiki Loves Folklore Tech Team has introduced tools to support organizers and participants: * '''Article List Generator by Topic''' – Helps identify articles available on English Wikipedia but missing in your local language Wikipedia. The tool allows customized filters and provides downloadable article lists in CSV and wikitable formats. * '''CampWiz''' – Enables communities to manage writing campaigns effectively, including jury-based evaluation. This will be the third year CampWiz is officially used for Feminism and Folklore. Both tools are now available for use in the campaign. '''[https://tools.wikilovesfolklore.org/ Click here to access the tools]''' ;Learn More & Get Support For detailed information about rules, timelines, and prizes, please visit the '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026 project page]]'''. If you have any questions or need assistance, feel free to reach out via: * '''[[:m:Talk:Feminism and Folklore 2026/Project Page|Meta talk page]]''' * Email us using details on the contact page. ;Join Us in upcoming office hours We look forward to your presence in the upcoming office hours where the community and the international Team can interact with each other and know more about the contest. resister yourself for the [[:m:Event:Wiki Loves Folklore 2026 Office Hours|office hours event]]. Thank you and all the best, '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026 International Team]]''' ---- ''Stay connected:'' [[File:B&W Facebook icon.png|link=https://www.facebook.com/feminismandfolklore/|30x30px]]&nbsp; [[File:B&W Twitter icon.png|link=https://twitter.com/wikifolklore|30x30px]] </div></div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೦೦, ೧೮ ಜನವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=29948401 --> == Feminism and Folklore 2026 – Community Organisers & Jury == Hello {{PAGENAME}}!, Thank you for taking the lead in organising '''Feminism and Folklore 2026''' in your community. We truly appreciate your efforts! To ensure a smooth and successful campaign, please make sure you have: * Fully completed all details on the [[:m:Feminism and Folklore 2026/Project Page|Feminism and Folklore 2026 Project Page]]: * Started promoting the campaign within your community. * Requested a local administrator to place a '''sitenotice''' about the campaign so users are notified. * Used the '''[https://tools.wikilovesfolklore.org/fnf/ Article List Generator Tool]''' and shared the generated article lists with your community. === Internet & Childcare Support === Community organisers and jury members who require '''internet and childcare support''' (non-mandatory, opt-in, request-only support) should fill the support request form '''by 22 February 2026'''. '''[https://docs.google.com/forms/d/e/1FAIpQLSeutXEF1yTnJfExWUYPIf6SkhcnTgul07BeI-biqT4RE_vsrA/viewform Link to the form]''' Requests submitted after this date will not be entertained. === Important Participation Guidelines === * Minimum article size: '''3000 bytes and 300 words''' (final decision may be set by local organisers). * If your country is not listed on the Article list generator tool, please contact us. === Community Engagement === * Keep your community active and motivated throughout the campaign. * Share your achievements and notable articles with us so we can highlight them globally. * In the support form, please indicate if you would like a '''quick coordination call after the campaign'''. Let’s make '''Feminism and Folklore''', under the banner of '''#WeTogether''', help bridge the '''gender gap''' and '''folklore gap''' on Wikipedia worldwide. 🌍✊ Thank you for your collaboration! ''If someone from your community organisers or jury has missed this message feel free to share this message with them.'' Feminism and Folklore International Team. –[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೦:೪೭, ೧೬ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=30083330 --> == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Next Steps and Feedback meeting for Feminism and Folklore Organizers == <div style="border:8px maroon ridge; padding:6px;"> [[File:Feminism and Folklore 2026 logo.svg|center|550px|frameless]] <div lang="en" dir="ltr" class="mw-content-ltr" style="padding: 1em 2em;"> <div style="text-align: center; width: 100%;">''{{int:please-translate}}''</div> Dear Organizer, I hope this message finds you well. First and foremost, on behalf of the International Team I want to extend my gratitude to you for your efforts in organizing the '''Feminism and Folklore 2026''' campaign on your local Wikipedia. Your contribution has been instrumental in bridging the gender and folk gap on Wikipedia, and we truly appreciate your dedication to this important cause. As the campaign has ended I wanted to inform you about the next steps. It's time to commence the jury process using the CampWiz or Fountain tool where your campaign was hosted. Please ensure that you update the details of the jury, campaign links and the names of organizers accurately on the [[:m:Feminism and Folklore 2026/Project Page|sign-up page]]. Once the jury process is completed, kindly update only the top 3 winners details on the [[:m:Feminism and Folklore 2026/Results|results page]] accordingly. The deadline for jury submission of results is '''April 30, 2026'''. However, if you find that the number of articles is high and you require more time, please don't hesitate to inform us via email or on campaign Meta Wiki talk page. We are more than willing to approve an extension if needed. Should you encounter any issues with the tools, please feel free to reach out to us on Telegram for assistance. Your feedback and progress updates are crucial for us to improve the campaign and better understand your community's insights. Therefore, we kindly ask you to spare just an hour to collectively share your progress and achievements with us during our '''[[:m:Event:Feminism and Folklore 2026 Post-Campaign Office Hour|community feedback session]]'''. Your input will greatly assist us in making the campaign more meaningful and impactful. Thank you once again for your hard work and dedication to the Feminism and Folklore campaign. Your efforts are deeply appreciated, and we look forward to hearing from you soon. Warm regards, [[User:Tiven2240|Tiven2240]] on behalf of Feminism and Folklore International Team <nowiki>#WeTogether</nowiki> </div></div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೭:೨೭, ೧೧ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=30391231 --> == Last Few Days: WikiConference India 2026 Scholarship Applications == <div lang="en" dir="ltr" class="mw-content-ltr"> ''{{int:please-translate}}'' Dear Wikimedian, We're happy to share that scholarship applications for '''WikiConference India 2026''' are currently open and the deadline is just around the corner. [[m:Special:MyLanguage/WikiConference India 2026|WikiConference India 2026]] is the fourth edition of the national-level conference that brings together Wikimedians and stakeholders engaged in Indic-language Wikimedia projects and the broader open knowledge movement across India and South Asia. The conference will take place in Kochi, Kerala, from 4–6 September 2026. * You can find the more information and the application form at the [[m:Special:MyLanguage/WikiConference India 2026/Scholarship|Scholarship page here at Meta wiki]] * '''Scholarship deadline: 15 April 2026, 11:59 PM IST''' With only a few days left, we warmly encourage you to apply if you haven’t already and kindly request you to share this with your community and encourage others to apply. For more information and regular updates, we encourage you to visit the conference Meta page. Warm regards, <br> on behalf of the WikiConference India 2026 Organising Team ''This message was sent with [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) on ೦೦:೦೦, ೧೨ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST)'' </div> <!-- Message sent by User:Gnoeee@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2026_active_users&oldid=30389801 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == Final Reminder: Submission of Local Winners for Feminism and Folklore 2026 == Dear Feminism and Folklore organiser, This is a friendly reminder that the deadline for submitting the names and details of your local winners for '''Feminism and Folklore 2026''' is '''5 June 2026'''. Please ensure that your community's [[:m:Feminism and Folklore 2026/Results|winner information]] is submitted before this date. Communities that do not provide their winner data by the deadline will unfortunately not be eligible for prize distribution, and the international organizing team will not be able to accommodate late submissions received after 5 June 2026. Thank you for your prompt attention to this matter and for your participation in the campaign. Best regards, '''Feminism and Folklore International Organizing Team''' --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೦೭:೩೯, ೩ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30627457 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Thank you for organising Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your dedication, coordination, and hard work played a vital role in making this year's [[:m:Feminism and Folklore 2026|Feminism and Folklore]] campaign successful. We truly appreciate the time and effort you invested in engaging your community and supporting participants. As we have completed the jury phase and begin planning for next year's campaign, we would like to learn from your experiences. Please complete the organisers' feedback form and share your insights, challenges, and suggestions on how we can improve the campaign. '''This feedback form is mandatory for all organisers''', as it helps us evaluate the campaign and make meaningful improvements for future editions. '''Form link [https://docs.google.com/forms/d/e/1FAIpQLSf6IsGAsxkTEWTwueyLg-gBjRVlY3g2kTNj3xRhDiILiQnQwQ/viewform?usp=dialog here]''' Please submit your response by '''25 June 2026'''. Thank you once again for your invaluable contributions and for helping make Feminism and Folklore a global success. We look forward to working with you again in the future! Regards, '''Feminism and Folklore Team''' --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೭:೩೫, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704175 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == [[Confédération Mondiale des Activités Subaquatiques]] == Hi, I noticed that you imported the article “Confédération Mondiale des Activités Subaquatiques” in May, but the text is still in English. Could you please take another look at this when you have a chance? Thanks! [[ಬಳಕೆದಾರೆ:Icodense|Icodense]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Icodense|ಪಾತೆರ್ಲೆ]]) ೧೫:೪೮, ೧೨ ಜುಲಾಯಿ ೨೦೨೬ (IST) PS: Same for [[The Partisan]]. == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == ಟೆಂಪ್ಲೇಟ್‌ದ ಬಗೆಟ್ == ಫತೇಪುರ್ ಸಿಕ್ರಿ ಲೇಕನೊಡು ಅರ್ತ ಆವಂದಿ ವಾಕ್ಯೊಲು ಸರಿ ಮಲ್ತ್‌ದ್ ಸರಿ ಮಲ್ಪೆರೆ ಬರೆತಾರ್, ದಯಮಲ್ತ್‌ದ್ ಅರ್ತ ಆವಂದಿನ ವಾಕ್ಯ ಒವ್ವು ಪಂಡ್ದ್ ತೋಜಪಾಲೆ ಸರಿ ಮಲ್ಪುಗ. ದನ್ಯವಾದೊಲು. [[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೧:೧೩, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) fny2jbmlkmqftovycel5s9zq6pohot4 366148 366142 2026-08-21T07:51:06Z ChiK 1136 /* ಟೆಂಪ್ಲೇಟ್‌ದ ಬಗೆಟ್ */ ಪ್ರತಿಕ್ರಿಯೆ 366148 wikitext text/x-wiki {{Template:Welcome|realName=|name=ChidanandaKampa}} == [[ಚಂದ್ರೆ]] == Hello, Is [https://tcy.wikipedia.org/w/index.php?title=%E0%B2%9A%E0%B2%82%E0%B2%A6%E0%B3%8D%E0%B2%B0%E0%B3%86&type=revision&diff=143298&oldid=143297 this] correct? Thanks, [[ಬಳಕೆದಾರೆ:Kwamikagami|Kwamikagami]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kwamikagami|ಪಾತೆರ್ಲೆ]]) ೧೯:೫೭, ೩೧ ಜನವರಿ ೨೦೨೨ (IST) == WikiConference India 2023: Program submissions and Scholarships form are now open == Dear Wikimedian, We are really glad to inform you that '''[[:m:WikiConference India 2023|WikiConference India 2023]]''' has been successfully funded and it will take place from 3 to 5 March 2023. The theme of the conference will be '''Strengthening the Bonds'''. We also have exciting updates about the Program and Scholarships. The applications for scholarships and program submissions are already open! You can find the form for scholarship '''[[:m:WikiConference India 2023/Scholarships|here]]''' and for program you can go '''[[:m:WikiConference India 2023/Program Submissions|here]]'''. For more information and regular updates please visit the Conference [[:m:WikiConference India 2023|Meta page]]. If you have something in mind you can write on [[:m:Talk:WikiConference India 2023|talk page]]. ‘‘‘Note’’’: Scholarship form and the Program submissions will be open from '''11 November 2022, 00:00 IST''' and the last date to submit is '''27 November 2022, 23:59 IST'''. Regards [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೧೬ ನವಂಬರ್ ೨೦೨೨ (IST) (on behalf of the WCI Organizing Committee) <!-- Message sent by User:Nitesh Gill@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2023_active_users,_scholarships_and_program&oldid=24082246 --> == WikiConference India 2023: Open Community Call and Extension of program and scholarship submissions deadline == Dear Wikimedian, Thank you for supporting Wiki Conference India 2023. We are humbled by the number of applications we have received and hope to learn more about the work that you all have been doing to take the movement forward. In order to offer flexibility, we have recently extended our deadline for the Program and Scholarships submission- you can find all the details on our [[:m:WikiConference India 2023|Meta Page]]. COT is working hard to ensure we bring together a conference that is truly meaningful and impactful for our movement and one that brings us all together. With an intent to be inclusive and transparent in our process, we are committed to organizing community sessions at regular intervals for sharing updates and to offer an opportunity to the community for engagement and review. Following the same, we are hosting the first Open Community Call on the 3rd of December, 2022. We wish to use this space to discuss the progress and answer any questions, concerns or clarifications, about the conference and the Program/Scholarships. Please add the following to your respective calendars and we look forward to seeing you on the call * '''WCI 2023 Open Community Call''' * '''Date''': 3rd December 2022 * '''Time''': 1800-1900 (IST) * '''Google Link'''': https://meet.google.com/cwa-bgwi-ryx Furthermore, we are pleased to share the email id of the conference contact@wikiconferenceindia.org which is where you could share any thoughts, inputs, suggestions, or questions and someone from the COT will reach out to you. Alternatively, leave us a message on the Conference [[:m:Talk:WikiConference India 2023|talk page]]. Regards [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೧:೫೧, ೨ ದಸಂಬರ್ ೨೦೨೨ (IST) On Behalf of, WCI 2023 Core organizing team. <!-- Message sent by User:Nitesh Gill@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2023_active_users,_scholarships_and_program&oldid=24083503 --> == Archiver bot == Hello. I was reviewing [[Special:Contribs/MABot|MABot]] logs and saw that [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ]] is now being archived at [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ ಪತ್ರಾಗಾರೊ]]. Is this intended? I'm asking as I'd rather avoid archiving in the wrong place. If this is now where you'd like to archive threads for that project, I can continue. Thanks in advance for your answer. Sincerely, [[ಬಳಕೆದಾರೆ:MarcoAurelio|MarcoAurelio]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MarcoAurelio|ಪಾತೆರ್ಲೆ]]) ೨೩:೨೫, ೮ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) :Thank you for your response this [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ ಪತ್ರಾಗಾರೊ]] is right place. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೨:೩೮, ೯ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) ::Thank you. I'll re-configure the bot. Sincerely, [[ಬಳಕೆದಾರೆ:MarcoAurelio|MarcoAurelio]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MarcoAurelio|ಪಾತೆರ್ಲೆ]]) ೦೫:೦೬, ೧೦ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) == Mahatma Gandhi 2023 edit-a-thon Barnstar Award == ''Sorry for writing this message in English - feel free to help us translating it'' {| style="background-color: #fdffe7; border: 1px solid #fceb92;" | rowspan="2" style="vertical-align: middle; padding: 5px;" |[[ಫೈಲ್:Special_Gold_Barnstar.png|100x100px]] | style="font-size: x-large; padding: 3px 3px 0 3px; height: 1.5em;" |'''Congratulations!!!''' |- | style="vertical-align: middle; padding: 3px;" |Thank you for contributing to the [[:meta:Karavali Wikimedians/Events/Mahatma Gandhi 2023 edit-a-thon|Mahatma Gandhi 2023 edit-a-thon]]. We are appreciative of your tireless efforts to create articles about Mahatma Gandhi as well as other Independence movements on your local Wikipedia. We are deeply inspired by your persistent efforts, dedication to improve articles. We are lucky to have amazing Wikimedian like you in our movement. Thank you for your contribution to Wikipedia. Hopefully, Wikipedia will continue to enrich your active constructive editing in the future. Thanks for your contribution <br/> '''Tamanpreet Kaur''' <br/> ''Punjabi Wikimedian''. [[ಬಳಕೆದಾರೆ:Tamanpreet Kaur|Tamanpreet Kaur]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Tamanpreet Kaur|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೧, ೧೯ ಅಕ್ಟೋಬರ್ ೨೦೨೩ (IST) |} == Organising Feminism and Folklore == [[File:Feminism and Folklore 2024 logo.svg | 350px | right]] Hello Community Organizers, Thank you for organising Feminism and Folklore writing competition on your wiki. We congratulate you in joining and celebrating our cultural heritage and promoting gender equality on Wikipedia. To encourage boost for the contributions of the participants, we're offering prizes for Feminism and Folklore local prizes. Each Wikipedia will have three local winners: *First Prize: $15 USD *Second Prize: $10 USD *Best Jury Article: $5 USD All this will be in '''gift voucher format only'''. Kindly inform your local community regarding these prizes and post them on the local project page The Best Jury Article will be chosen by the jury based on how unique the article is aligned with the theme. The jury will review all submissions and decide the winner together, making sure it's fair. These articles will also be featured on our social media handles. We're also providing internet and childcare support to the first 50 organizers and Jury members for who request for it. Remember, only 50 organizers will get this support, and it's given on a first-come, first-served basis. The registration form will close after 50 registrations, and the deadline is March 15, 2024. This support is optional and not compulsory, so if you're interested, fill out the form [https://docs.google.com/forms/d/e/1FAIpQLSdnytyact-HR6DvsWwnrVeWuzMfuNH1dSjpF24m6od-f3LzZQ/viewform here]. Each organizer/jury who gets support will receive $30 USD in gift voucher format, even if they're involved in more than one wiki. No dual support will be provided if you have signed up in more than one language. This support is meant to appreciate your volunteer support for the contest. We also invite all organizers and jury members to join us for office hours on '''Saturday, March 2, 2024'''. This session will help you understand the jury process for both contests and give you a chance to ask questions. More details are on [https://meta.wikimedia.org/wiki/Wiki_Loves_Folklore_2024_Office_Hour_2 meta page]. Let's celebrate our different cultures and work towards gender equality on Wikipedia! Best regards, Rockpeterson [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೧:೨೬, ೨೯ ಪೆಬ್ರವರಿ ೨೦೨೪ (IST) <!-- Message sent by User:Rockpeterson@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Rockpeterson/fnf2024golbal&oldid=26304232 --> == Translation request == Hello, ChiK. Can you translate and upload the articles [[:en:Azerbaijani manat]], [[:en:Azerbaijani manat sign]] and [[:en:Campi Flegrei]] in Tulu Wikipedia? Yours sincerely, [[ಬಳಕೆದಾರೆ:Oirattas|Oirattas]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Oirattas|ಪಾತೆರ್ಲೆ]]) ೨೧:೫೬, ೧೫ ಸಪ್ಟಂಬರ್ ೨೦೨೪ (IST) :Thank you very much for the [[ಅಜೆರ್ಬೈಜಾನಿ ಮನಾತ್ ಗುರ್ತ]] article. Can you also translate and upload the articles [[:en:Azerbaijani manat]] and [[:en:Campi Flegrei]] in Tulu Wikipedia (and also all these three articles in Kannada Wikipedia)? [[ಬಳಕೆದಾರೆ:Oirattas|Oirattas]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Oirattas|ಪಾತೆರ್ಲೆ]]) ೨೧:೧೧, ೧೨ ಅಕ್ಟೋಬರ್ ೨೦೨೪ (IST) == Organising Tuluvas Aati Month == [[File:Tuluvas Aati Month banner logo.svg| 150px | right]] Hello Organizers, We want to extend our heartfelt gratitude to you for making the '''[[m:Tuluvas Aati Month|Tuluvas Aati Month]]''' event a success. Your time, dedication, and expertise brought this cultural celebration to life, creating meaningful experiences for everyone involved. To help us improve future events and celebrate the efforts of everyone. Each organizer/jury who gets support will receive in gift card format. please fill out [https://docs.google.com/forms/d/e/1FAIpQLSd3RWB1kvSyR77kYaZtI-S1T9da48Cd7j6giMb6naSDXcYl9Q/viewform this form]. Best regards,<br> '''Tuluvas Aati Month Team'''. [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೮:೫೬, ೧ ನವಂಬರ್ ೨೦೨೪ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tuluvas_Aati_Month/JuryList&oldid=27685549 --> == Invitation to Participate in the Wikimedia SAARC Conference Community Engagement Survey == Dear Community Members, I hope this message finds you well. Please excuse the use of English; we encourage translations into your local languages to ensure inclusivity. We are conducting a Community Engagement Survey to assess the sentiments, needs, and interests of South Asian Wikimedia communities in organizing the inaugural Wikimedia SAARC Regional Conference, proposed to be held in Kathmandu, Nepal. This initiative aims to bring together participants from eight nations to collaborate towards shared goals. Your insights will play a vital role in shaping the event's focus, identifying priorities, and guiding the strategic planning for this landmark conference. Survey Link: https://forms.gle/en8qSuCvaSxQVD7K6 We kindly request you to dedicate a few moments to complete the survey. Your feedback will significantly contribute to ensuring this conference addresses the community's needs and aspirations. Deadline to Submit the Survey: 20 January 2025 Your participation is crucial in shaping the future of the Wikimedia SAARC community and fostering regional collaboration. Thank you for your time and valuable input. Warm regards,<br> [[:m:User:Biplab Anand|Biplab Anand]] <!-- Message sent by User:Biplab Anand@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Biplab_Anand/lists&oldid=28078122 --> == Thank you for being a medical contributors! == <div lang="en" dir="ltr" class="mw-content-ltr"> {| style="background-color: #fdffe7; border: 1px solid #fceb92;" |rowspan="2" style="vertical-align: middle; padding: 5px;" | [[File:Wiki Project Med Foundation logo.svg|130px]] |style="font-size: x-large; padding: 3px 3px 0 3px; height: 1.5em;" |'''The 2024 Cure Award''' |- | style="vertical-align: middle; padding: 3px;" |In 2024 you '''[[mdwiki:WikiProjectMed:WikiProject_Medicine/Stats/Top_medical_editors_2024_(all)|were one of the top medical editors in your language]]'''. Thank you from [[m:WikiProject_Med|Wiki Project Med]] for helping bring free, complete, accurate, up-to-date health information to the public. We really appreciate you and the vital work you do! Wiki Project Med Foundation is a [[meta:Wikimedia_thematic_organizations|thematic organization]] whose mission is to improve our health content. '''[[meta:Wiki_Project_Med#People_interested|Consider joining for 2025]]''', there are no associated costs. Additionally one of our primary efforts revolves around translating health content. We invite you to '''[https://mdwiki.toolforge.org/Translation_Dashboard/index.php try our new workflow]''' if you have not already. Our dashboard automatically [https://mdwiki.toolforge.org/Translation_Dashboard/leaderboard.php collects statistics] of your efforts and we are working on [https://mdwiki.toolforge.org/fixwikirefs.php tools to automatically improve formating]. |} Thanks again :-) -- [[mdwiki:User:Doc_James|<span style="color:#0000f1">'''Doc James'''</span>]] along with the rest of the team at '''[[m:WikiProject_Med|Wiki Project Med Foundation]]''' ೧೧:೫೪, ೨೬ ಜನವರಿ ೨೦೨೫ (IST) </div> <!-- Message sent by User:Doc James@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Top_Other_Language_Editors_2024&oldid=28172893 --> == Feminism and Folklore 2025: Important Updates for Organizers & Jury == Hello Community Organizers and Jury, Thank you for organising Feminism and Folklore writing competition on your wiki. Feminism and Folklore is the largest Wikipedia contest organized by community members. We congratulate you in joining and celebrating our cultural heritage and promoting gender equality on Wikipedia. To encourage boost for the contributions of the participants, we're offering prizes for Feminism and Folklore local prizes. Each Wikipedia will have three local winners: # First Prize: $25 USD # Second Prize: $20 USD # Best Jury Article: $15 USD All this will be in '''gift voucher format only'''. Prizes will only be given to users who have more than 5 accepted articles. No prizes will be given for users winning below 5 accepted articles. Kindly inform your local community regarding these prizes and post them on the local project page The Best Jury Article will be chosen by the jury based on how unique the article is aligned with the theme. The jury will review all submissions and decide the winner together, making sure it's fair. These articles will also be featured on our social media handles. We're also providing internet and childcare support to the first 75 organizers and Jury members for those who request for it. Remember, only 75 organizers will get this support, and it's given on a first-come, first-served basis. The registration form will close after 75 registrations, and the deadline is <nowiki>'''</nowiki>March 5, 2025<nowiki>'''</nowiki>. This support is optional and not compulsory, so if you're interested, fill out the [https://docs.google.com/forms/d/e/1FAIpQLSeum8md6FqHY1ISWRLW5bqOAv_lcd1tpVtMMZfWKRDU_IffLQ/viewform?usp=dialog Form] Each organizer/jury who gets support will receive $40 USD in gift voucher format, even if they're involved in more than one wiki. No dual support will be provided if you have signed up in more than one language. This support is meant to appreciate your volunteer support for the contest. We also invite all organizers and jury members to join us for Advocacy session on '''Saturday, Feb 28, 2025'''. This session will help you understand the jury process for both contests and give you a chance to ask questions. More details are on [[meta:Event:Telling untold stories: How to document gendered narratives in Folklore on Wikipedia|Event:Telling untold stories: How to document gendered narratives in Folklore on Wikipedia - Meta]] Let's celebrate our different cultures and work towards gender equality on Wikipedia! Best regards, Stella and Tiven Wiki loves folklore international team [[User:SAgbley|SAgbley]] ([[User talk:SAgbley|talk]]) 04:39, 25 February 2025 (UTC) <!-- Message sent by User:Joris Darlington Quarshie@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Joris_Darlington_Quarshie/Community_Prizes&oldid=28309519 --> == Final reminder that the deadline for submitting your Feminism and Folklore 2025 results == <div style="border:8px maroon ridge;padding:6px;> Hello {{PAGENAME}} [[File:Feminism and Folklore logo.svg | right | frameless]] I hope this message finds you well. This is a final reminder that the deadline for submitting your Feminism and Folklore 2025 results on Meta has elapsed. For participant prizes to be processed, your community must submit the necessary information as soon as possible. Please use the following link to submit your results: [https://meta.wikimedia.org/wiki/Feminism_and_Folklore_2025/Results Submit your results] We have extended the {{font color||yellow|'''submission deadline to 12 June 2025'''.}} Kindly note that failure to submit by this date will result in your group’s participants not receiving any prizes from us. Thank you for your attention to this important matter and for your continued contributions to the Feminism and Folklore campaign. Warm regards,<br> Stella<br> On behalf of Feminism and Folklore Team<br> [[User:MediaWiki message delivery|MediaWiki message delivery]] ([[User talk:MediaWiki message delivery|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 12:39, 08 June 2025 (UTC) </div> <!-- Message sent by User:Joris Darlington Quarshie@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Template:FnF_2025_reminder_list&oldid=28842648 --> == Notice of expiration of your interface-admin right == <div dir="ltr">Hi, as part of [[:m:Special:MyLanguage/Global reminder bot|Global reminder bot]], this is an automated reminder to let you know that your permission "interface-admin" (ಅಂತರಮುಕ ನಿರ್ವಾಹಕೆರ್) will expire on 2025-11-29 15:03:18. Please renew this right if you would like to continue using it. <i>In other languages: [[:m:Special:MyLanguage/Global reminder bot/Messages/default|click here]]</i> [[ಬಳಕೆದಾರೆ:Leaderbot|Leaderbot]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Leaderbot|ಪಾತೆರ್ಲೆ]]) ೦೧:೧೩, ೨೩ ನವಂಬರ್ ೨೦೨೫ (IST)</div> == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Feminism and Folklore 2026 starts soon == <div style="border:8px maroon ridge;padding:6px;"> [[File:Feminism and Folklore 2026 logo.svg|centre|550px|frameless]] ::<div lang="en" dir="ltr" class="mw-content-ltr"> <div style="text-align: center; width: 100%;">''{{int:please-translate}}''</div> ;Invitation to Organize Feminism and Folklore 2026 Dear Wikimedian, We thank you for taking an initiative to organize the '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]''' writing competition on your local Wikipedia. The international campaign will run from '''1 February to 31 March 2026''' and aims to improve coverage of feminism, women’s histories, gender-related topics, and folk culture across Wikipedia projects. ;About the Campaign '''Feminism and Folklore''' is a global writing initiative that complements the '''[[:c:Commons:Wiki Loves Folklore 2026|Wiki Loves Folklore]]''' photography competition. While Wiki Loves Folklore focuses on visual documentation, this writing campaign addresses the '''gender gap on Wikipedia''' by improving encyclopedic content related to folk culture and marginalized voices. ;What Can Participants Write About? Communities can contribute by creating, expanding, or translating articles related to: * Folk festivals, rituals, and celebrations * Folk dances, music, and traditional performances * Women and queer figures in folklore * Women in mythology and oral traditions * Women warriors, witches, and witch-hunting narratives * Fairy tales, folk stories, and legends * Folk games, sports, and cultural practices Participants may work from curated article lists or generate new article suggestions using campaign tools. ;Checklist for Organizers Organizers are requested to complete the following steps to register their community: # Create a local project page on your wiki [[:m:Feminism and Folklore/Sample|(see sample)]] # Set up the campaign using the '''CampWiz''' tool # Prepare a local article list and clearly mention: #* Campaign timeline #* Local and international prizes # Request a site notice from local administrators [[:mr:Template:SN-FNF|(see sample)]] # Add your local project page and CampWiz link to the '''[[:m:Feminism and Folklore 2026/Project Page|Meta project page]]''' ;Campaign Tools The Wiki Loves Folklore Tech Team has introduced tools to support organizers and participants: * '''Article List Generator by Topic''' – Helps identify articles available on English Wikipedia but missing in your local language Wikipedia. The tool allows customized filters and provides downloadable article lists in CSV and wikitable formats. * '''CampWiz''' – Enables communities to manage writing campaigns effectively, including jury-based evaluation. This will be the third year CampWiz is officially used for Feminism and Folklore. Both tools are now available for use in the campaign. '''[https://tools.wikilovesfolklore.org/ Click here to access the tools]''' ;Learn More & Get Support For detailed information about rules, timelines, and prizes, please visit the '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026 project page]]'''. If you have any questions or need assistance, feel free to reach out via: * '''[[:m:Talk:Feminism and Folklore 2026/Project Page|Meta talk page]]''' * Email us using details on the contact page. ;Join Us in upcoming office hours We look forward to your presence in the upcoming office hours where the community and the international Team can interact with each other and know more about the contest. resister yourself for the [[:m:Event:Wiki Loves Folklore 2026 Office Hours|office hours event]]. Thank you and all the best, '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026 International Team]]''' ---- ''Stay connected:'' [[File:B&W Facebook icon.png|link=https://www.facebook.com/feminismandfolklore/|30x30px]]&nbsp; [[File:B&W Twitter icon.png|link=https://twitter.com/wikifolklore|30x30px]] </div></div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೦೦, ೧೮ ಜನವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=29948401 --> == Feminism and Folklore 2026 – Community Organisers & Jury == Hello {{PAGENAME}}!, Thank you for taking the lead in organising '''Feminism and Folklore 2026''' in your community. We truly appreciate your efforts! To ensure a smooth and successful campaign, please make sure you have: * Fully completed all details on the [[:m:Feminism and Folklore 2026/Project Page|Feminism and Folklore 2026 Project Page]]: * Started promoting the campaign within your community. * Requested a local administrator to place a '''sitenotice''' about the campaign so users are notified. * Used the '''[https://tools.wikilovesfolklore.org/fnf/ Article List Generator Tool]''' and shared the generated article lists with your community. === Internet & Childcare Support === Community organisers and jury members who require '''internet and childcare support''' (non-mandatory, opt-in, request-only support) should fill the support request form '''by 22 February 2026'''. '''[https://docs.google.com/forms/d/e/1FAIpQLSeutXEF1yTnJfExWUYPIf6SkhcnTgul07BeI-biqT4RE_vsrA/viewform Link to the form]''' Requests submitted after this date will not be entertained. === Important Participation Guidelines === * Minimum article size: '''3000 bytes and 300 words''' (final decision may be set by local organisers). * If your country is not listed on the Article list generator tool, please contact us. === Community Engagement === * Keep your community active and motivated throughout the campaign. * Share your achievements and notable articles with us so we can highlight them globally. * In the support form, please indicate if you would like a '''quick coordination call after the campaign'''. Let’s make '''Feminism and Folklore''', under the banner of '''#WeTogether''', help bridge the '''gender gap''' and '''folklore gap''' on Wikipedia worldwide. 🌍✊ Thank you for your collaboration! ''If someone from your community organisers or jury has missed this message feel free to share this message with them.'' Feminism and Folklore International Team. –[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೦:೪೭, ೧೬ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=30083330 --> == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Next Steps and Feedback meeting for Feminism and Folklore Organizers == <div style="border:8px maroon ridge; padding:6px;"> [[File:Feminism and Folklore 2026 logo.svg|center|550px|frameless]] <div lang="en" dir="ltr" class="mw-content-ltr" style="padding: 1em 2em;"> <div style="text-align: center; width: 100%;">''{{int:please-translate}}''</div> Dear Organizer, I hope this message finds you well. First and foremost, on behalf of the International Team I want to extend my gratitude to you for your efforts in organizing the '''Feminism and Folklore 2026''' campaign on your local Wikipedia. Your contribution has been instrumental in bridging the gender and folk gap on Wikipedia, and we truly appreciate your dedication to this important cause. As the campaign has ended I wanted to inform you about the next steps. It's time to commence the jury process using the CampWiz or Fountain tool where your campaign was hosted. Please ensure that you update the details of the jury, campaign links and the names of organizers accurately on the [[:m:Feminism and Folklore 2026/Project Page|sign-up page]]. Once the jury process is completed, kindly update only the top 3 winners details on the [[:m:Feminism and Folklore 2026/Results|results page]] accordingly. The deadline for jury submission of results is '''April 30, 2026'''. However, if you find that the number of articles is high and you require more time, please don't hesitate to inform us via email or on campaign Meta Wiki talk page. We are more than willing to approve an extension if needed. Should you encounter any issues with the tools, please feel free to reach out to us on Telegram for assistance. Your feedback and progress updates are crucial for us to improve the campaign and better understand your community's insights. Therefore, we kindly ask you to spare just an hour to collectively share your progress and achievements with us during our '''[[:m:Event:Feminism and Folklore 2026 Post-Campaign Office Hour|community feedback session]]'''. Your input will greatly assist us in making the campaign more meaningful and impactful. Thank you once again for your hard work and dedication to the Feminism and Folklore campaign. Your efforts are deeply appreciated, and we look forward to hearing from you soon. Warm regards, [[User:Tiven2240|Tiven2240]] on behalf of Feminism and Folklore International Team <nowiki>#WeTogether</nowiki> </div></div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೭:೨೭, ೧೧ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=30391231 --> == Last Few Days: WikiConference India 2026 Scholarship Applications == <div lang="en" dir="ltr" class="mw-content-ltr"> ''{{int:please-translate}}'' Dear Wikimedian, We're happy to share that scholarship applications for '''WikiConference India 2026''' are currently open and the deadline is just around the corner. [[m:Special:MyLanguage/WikiConference India 2026|WikiConference India 2026]] is the fourth edition of the national-level conference that brings together Wikimedians and stakeholders engaged in Indic-language Wikimedia projects and the broader open knowledge movement across India and South Asia. The conference will take place in Kochi, Kerala, from 4–6 September 2026. * You can find the more information and the application form at the [[m:Special:MyLanguage/WikiConference India 2026/Scholarship|Scholarship page here at Meta wiki]] * '''Scholarship deadline: 15 April 2026, 11:59 PM IST''' With only a few days left, we warmly encourage you to apply if you haven’t already and kindly request you to share this with your community and encourage others to apply. For more information and regular updates, we encourage you to visit the conference Meta page. Warm regards, <br> on behalf of the WikiConference India 2026 Organising Team ''This message was sent with [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) on ೦೦:೦೦, ೧೨ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST)'' </div> <!-- Message sent by User:Gnoeee@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2026_active_users&oldid=30389801 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == Final Reminder: Submission of Local Winners for Feminism and Folklore 2026 == Dear Feminism and Folklore organiser, This is a friendly reminder that the deadline for submitting the names and details of your local winners for '''Feminism and Folklore 2026''' is '''5 June 2026'''. Please ensure that your community's [[:m:Feminism and Folklore 2026/Results|winner information]] is submitted before this date. Communities that do not provide their winner data by the deadline will unfortunately not be eligible for prize distribution, and the international organizing team will not be able to accommodate late submissions received after 5 June 2026. Thank you for your prompt attention to this matter and for your participation in the campaign. Best regards, '''Feminism and Folklore International Organizing Team''' --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೦೭:೩೯, ೩ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30627457 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Thank you for organising Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your dedication, coordination, and hard work played a vital role in making this year's [[:m:Feminism and Folklore 2026|Feminism and Folklore]] campaign successful. We truly appreciate the time and effort you invested in engaging your community and supporting participants. As we have completed the jury phase and begin planning for next year's campaign, we would like to learn from your experiences. Please complete the organisers' feedback form and share your insights, challenges, and suggestions on how we can improve the campaign. '''This feedback form is mandatory for all organisers''', as it helps us evaluate the campaign and make meaningful improvements for future editions. '''Form link [https://docs.google.com/forms/d/e/1FAIpQLSf6IsGAsxkTEWTwueyLg-gBjRVlY3g2kTNj3xRhDiILiQnQwQ/viewform?usp=dialog here]''' Please submit your response by '''25 June 2026'''. Thank you once again for your invaluable contributions and for helping make Feminism and Folklore a global success. We look forward to working with you again in the future! Regards, '''Feminism and Folklore Team''' --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೭:೩೫, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704175 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == [[Confédération Mondiale des Activités Subaquatiques]] == Hi, I noticed that you imported the article “Confédération Mondiale des Activités Subaquatiques” in May, but the text is still in English. Could you please take another look at this when you have a chance? Thanks! [[ಬಳಕೆದಾರೆ:Icodense|Icodense]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Icodense|ಪಾತೆರ್ಲೆ]]) ೧೫:೪೮, ೧೨ ಜುಲಾಯಿ ೨೦೨೬ (IST) PS: Same for [[The Partisan]]. == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == ಟೆಂಪ್ಲೇಟ್‌ದ ಬಗೆಟ್ == ಫತೇಪುರ್ ಸಿಕ್ರಿ ಲೇಕನೊಡು ಅರ್ತ ಆವಂದಿ ವಾಕ್ಯೊಲು ಸರಿ ಮಲ್ತ್‌ದ್ ಸರಿ ಮಲ್ಪೆರೆ ಬರೆತಾರ್, ದಯಮಲ್ತ್‌ದ್ ಅರ್ತ ಆವಂದಿನ ವಾಕ್ಯ ಒವ್ವು ಪಂಡ್ದ್ ತೋಜಪಾಲೆ ಸರಿ ಮಲ್ಪುಗ. ದನ್ಯವಾದೊಲು. [[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೧:೧೩, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :@[[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]]: [[ಫತೇಪುರ್ ಸಿಕ್ರಿ]] [[Special:Diff/366101]] ಕೆಲವೊಂಜೆನ್ ಸರಿ ಮಲ್ತ್‌ದ; ಲೇಕನೊ ಇತ್ತೆ ಸರಿಯಾದ್ ಇತ್ತ್ಂಡ, ಏರೆಗ್ಲಾ ಈ <code><nowiki>{{Copy edit}}</nowiki></code> ನಿರ್ವಹಣೆದ ಟೆಂಪ್ಲೇಟ್‌ನ್ ದೆತ್ತ್ ಪಾಡ್‌ದ್ ಕೊಡುಗೆ ಕೊರೊಲಿ. ಇರೆಗ್ ಸೊಲ್ಮೆಲು, [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೩:೨೧, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) kjo86je128j45xvhwlmwemfrmdrg61h 366150 366148 2026-08-21T08:13:57Z Kavitha G. Kana 1040 /* ಟೆಂಪ್ಲೇಟ್‌ದ ಬಗೆಟ್ */ ಪ್ರತಿಕ್ರಿಯೆ 366150 wikitext text/x-wiki {{Template:Welcome|realName=|name=ChidanandaKampa}} == [[ಚಂದ್ರೆ]] == Hello, Is [https://tcy.wikipedia.org/w/index.php?title=%E0%B2%9A%E0%B2%82%E0%B2%A6%E0%B3%8D%E0%B2%B0%E0%B3%86&type=revision&diff=143298&oldid=143297 this] correct? Thanks, [[ಬಳಕೆದಾರೆ:Kwamikagami|Kwamikagami]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kwamikagami|ಪಾತೆರ್ಲೆ]]) ೧೯:೫೭, ೩೧ ಜನವರಿ ೨೦೨೨ (IST) == WikiConference India 2023: Program submissions and Scholarships form are now open == Dear Wikimedian, We are really glad to inform you that '''[[:m:WikiConference India 2023|WikiConference India 2023]]''' has been successfully funded and it will take place from 3 to 5 March 2023. The theme of the conference will be '''Strengthening the Bonds'''. We also have exciting updates about the Program and Scholarships. The applications for scholarships and program submissions are already open! You can find the form for scholarship '''[[:m:WikiConference India 2023/Scholarships|here]]''' and for program you can go '''[[:m:WikiConference India 2023/Program Submissions|here]]'''. For more information and regular updates please visit the Conference [[:m:WikiConference India 2023|Meta page]]. If you have something in mind you can write on [[:m:Talk:WikiConference India 2023|talk page]]. ‘‘‘Note’’’: Scholarship form and the Program submissions will be open from '''11 November 2022, 00:00 IST''' and the last date to submit is '''27 November 2022, 23:59 IST'''. Regards [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೧೬ ನವಂಬರ್ ೨೦೨೨ (IST) (on behalf of the WCI Organizing Committee) <!-- Message sent by User:Nitesh Gill@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2023_active_users,_scholarships_and_program&oldid=24082246 --> == WikiConference India 2023: Open Community Call and Extension of program and scholarship submissions deadline == Dear Wikimedian, Thank you for supporting Wiki Conference India 2023. We are humbled by the number of applications we have received and hope to learn more about the work that you all have been doing to take the movement forward. In order to offer flexibility, we have recently extended our deadline for the Program and Scholarships submission- you can find all the details on our [[:m:WikiConference India 2023|Meta Page]]. COT is working hard to ensure we bring together a conference that is truly meaningful and impactful for our movement and one that brings us all together. With an intent to be inclusive and transparent in our process, we are committed to organizing community sessions at regular intervals for sharing updates and to offer an opportunity to the community for engagement and review. Following the same, we are hosting the first Open Community Call on the 3rd of December, 2022. We wish to use this space to discuss the progress and answer any questions, concerns or clarifications, about the conference and the Program/Scholarships. Please add the following to your respective calendars and we look forward to seeing you on the call * '''WCI 2023 Open Community Call''' * '''Date''': 3rd December 2022 * '''Time''': 1800-1900 (IST) * '''Google Link'''': https://meet.google.com/cwa-bgwi-ryx Furthermore, we are pleased to share the email id of the conference contact@wikiconferenceindia.org which is where you could share any thoughts, inputs, suggestions, or questions and someone from the COT will reach out to you. Alternatively, leave us a message on the Conference [[:m:Talk:WikiConference India 2023|talk page]]. Regards [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೧:೫೧, ೨ ದಸಂಬರ್ ೨೦೨೨ (IST) On Behalf of, WCI 2023 Core organizing team. <!-- Message sent by User:Nitesh Gill@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2023_active_users,_scholarships_and_program&oldid=24083503 --> == Archiver bot == Hello. I was reviewing [[Special:Contribs/MABot|MABot]] logs and saw that [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ]] is now being archived at [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ ಪತ್ರಾಗಾರೊ]]. Is this intended? I'm asking as I'd rather avoid archiving in the wrong place. If this is now where you'd like to archive threads for that project, I can continue. Thanks in advance for your answer. Sincerely, [[ಬಳಕೆದಾರೆ:MarcoAurelio|MarcoAurelio]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MarcoAurelio|ಪಾತೆರ್ಲೆ]]) ೨೩:೨೫, ೮ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) :Thank you for your response this [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ ಪತ್ರಾಗಾರೊ]] is right place. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೨:೩೮, ೯ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) ::Thank you. I'll re-configure the bot. Sincerely, [[ಬಳಕೆದಾರೆ:MarcoAurelio|MarcoAurelio]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MarcoAurelio|ಪಾತೆರ್ಲೆ]]) ೦೫:೦೬, ೧೦ ಪೆಬ್ರವರಿ ೨೦೨೩ (IST) == Mahatma Gandhi 2023 edit-a-thon Barnstar Award == ''Sorry for writing this message in English - feel free to help us translating it'' {| style="background-color: #fdffe7; border: 1px solid #fceb92;" | rowspan="2" style="vertical-align: middle; padding: 5px;" |[[ಫೈಲ್:Special_Gold_Barnstar.png|100x100px]] | style="font-size: x-large; padding: 3px 3px 0 3px; height: 1.5em;" |'''Congratulations!!!''' |- | style="vertical-align: middle; padding: 3px;" |Thank you for contributing to the [[:meta:Karavali Wikimedians/Events/Mahatma Gandhi 2023 edit-a-thon|Mahatma Gandhi 2023 edit-a-thon]]. We are appreciative of your tireless efforts to create articles about Mahatma Gandhi as well as other Independence movements on your local Wikipedia. We are deeply inspired by your persistent efforts, dedication to improve articles. We are lucky to have amazing Wikimedian like you in our movement. Thank you for your contribution to Wikipedia. Hopefully, Wikipedia will continue to enrich your active constructive editing in the future. Thanks for your contribution <br/> '''Tamanpreet Kaur''' <br/> ''Punjabi Wikimedian''. [[ಬಳಕೆದಾರೆ:Tamanpreet Kaur|Tamanpreet Kaur]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Tamanpreet Kaur|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೧, ೧೯ ಅಕ್ಟೋಬರ್ ೨೦೨೩ (IST) |} == Organising Feminism and Folklore == [[File:Feminism and Folklore 2024 logo.svg | 350px | right]] Hello Community Organizers, Thank you for organising Feminism and Folklore writing competition on your wiki. We congratulate you in joining and celebrating our cultural heritage and promoting gender equality on Wikipedia. To encourage boost for the contributions of the participants, we're offering prizes for Feminism and Folklore local prizes. Each Wikipedia will have three local winners: *First Prize: $15 USD *Second Prize: $10 USD *Best Jury Article: $5 USD All this will be in '''gift voucher format only'''. Kindly inform your local community regarding these prizes and post them on the local project page The Best Jury Article will be chosen by the jury based on how unique the article is aligned with the theme. The jury will review all submissions and decide the winner together, making sure it's fair. These articles will also be featured on our social media handles. We're also providing internet and childcare support to the first 50 organizers and Jury members for who request for it. Remember, only 50 organizers will get this support, and it's given on a first-come, first-served basis. The registration form will close after 50 registrations, and the deadline is March 15, 2024. This support is optional and not compulsory, so if you're interested, fill out the form [https://docs.google.com/forms/d/e/1FAIpQLSdnytyact-HR6DvsWwnrVeWuzMfuNH1dSjpF24m6od-f3LzZQ/viewform here]. Each organizer/jury who gets support will receive $30 USD in gift voucher format, even if they're involved in more than one wiki. No dual support will be provided if you have signed up in more than one language. This support is meant to appreciate your volunteer support for the contest. We also invite all organizers and jury members to join us for office hours on '''Saturday, March 2, 2024'''. This session will help you understand the jury process for both contests and give you a chance to ask questions. More details are on [https://meta.wikimedia.org/wiki/Wiki_Loves_Folklore_2024_Office_Hour_2 meta page]. Let's celebrate our different cultures and work towards gender equality on Wikipedia! Best regards, Rockpeterson [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೧:೨೬, ೨೯ ಪೆಬ್ರವರಿ ೨೦೨೪ (IST) <!-- Message sent by User:Rockpeterson@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Rockpeterson/fnf2024golbal&oldid=26304232 --> == Translation request == Hello, ChiK. Can you translate and upload the articles [[:en:Azerbaijani manat]], [[:en:Azerbaijani manat sign]] and [[:en:Campi Flegrei]] in Tulu Wikipedia? Yours sincerely, [[ಬಳಕೆದಾರೆ:Oirattas|Oirattas]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Oirattas|ಪಾತೆರ್ಲೆ]]) ೨೧:೫೬, ೧೫ ಸಪ್ಟಂಬರ್ ೨೦೨೪ (IST) :Thank you very much for the [[ಅಜೆರ್ಬೈಜಾನಿ ಮನಾತ್ ಗುರ್ತ]] article. Can you also translate and upload the articles [[:en:Azerbaijani manat]] and [[:en:Campi Flegrei]] in Tulu Wikipedia (and also all these three articles in Kannada Wikipedia)? [[ಬಳಕೆದಾರೆ:Oirattas|Oirattas]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Oirattas|ಪಾತೆರ್ಲೆ]]) ೨೧:೧೧, ೧೨ ಅಕ್ಟೋಬರ್ ೨೦೨೪ (IST) == Organising Tuluvas Aati Month == [[File:Tuluvas Aati Month banner logo.svg| 150px | right]] Hello Organizers, We want to extend our heartfelt gratitude to you for making the '''[[m:Tuluvas Aati Month|Tuluvas Aati Month]]''' event a success. Your time, dedication, and expertise brought this cultural celebration to life, creating meaningful experiences for everyone involved. To help us improve future events and celebrate the efforts of everyone. Each organizer/jury who gets support will receive in gift card format. please fill out [https://docs.google.com/forms/d/e/1FAIpQLSd3RWB1kvSyR77kYaZtI-S1T9da48Cd7j6giMb6naSDXcYl9Q/viewform this form]. Best regards,<br> '''Tuluvas Aati Month Team'''. [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೮:೫೬, ೧ ನವಂಬರ್ ೨೦೨೪ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tuluvas_Aati_Month/JuryList&oldid=27685549 --> == Invitation to Participate in the Wikimedia SAARC Conference Community Engagement Survey == Dear Community Members, I hope this message finds you well. Please excuse the use of English; we encourage translations into your local languages to ensure inclusivity. We are conducting a Community Engagement Survey to assess the sentiments, needs, and interests of South Asian Wikimedia communities in organizing the inaugural Wikimedia SAARC Regional Conference, proposed to be held in Kathmandu, Nepal. This initiative aims to bring together participants from eight nations to collaborate towards shared goals. Your insights will play a vital role in shaping the event's focus, identifying priorities, and guiding the strategic planning for this landmark conference. Survey Link: https://forms.gle/en8qSuCvaSxQVD7K6 We kindly request you to dedicate a few moments to complete the survey. Your feedback will significantly contribute to ensuring this conference addresses the community's needs and aspirations. Deadline to Submit the Survey: 20 January 2025 Your participation is crucial in shaping the future of the Wikimedia SAARC community and fostering regional collaboration. Thank you for your time and valuable input. Warm regards,<br> [[:m:User:Biplab Anand|Biplab Anand]] <!-- Message sent by User:Biplab Anand@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Biplab_Anand/lists&oldid=28078122 --> == Thank you for being a medical contributors! == <div lang="en" dir="ltr" class="mw-content-ltr"> {| style="background-color: #fdffe7; border: 1px solid #fceb92;" |rowspan="2" style="vertical-align: middle; padding: 5px;" | [[File:Wiki Project Med Foundation logo.svg|130px]] |style="font-size: x-large; padding: 3px 3px 0 3px; height: 1.5em;" |'''The 2024 Cure Award''' |- | style="vertical-align: middle; padding: 3px;" |In 2024 you '''[[mdwiki:WikiProjectMed:WikiProject_Medicine/Stats/Top_medical_editors_2024_(all)|were one of the top medical editors in your language]]'''. Thank you from [[m:WikiProject_Med|Wiki Project Med]] for helping bring free, complete, accurate, up-to-date health information to the public. We really appreciate you and the vital work you do! Wiki Project Med Foundation is a [[meta:Wikimedia_thematic_organizations|thematic organization]] whose mission is to improve our health content. '''[[meta:Wiki_Project_Med#People_interested|Consider joining for 2025]]''', there are no associated costs. Additionally one of our primary efforts revolves around translating health content. We invite you to '''[https://mdwiki.toolforge.org/Translation_Dashboard/index.php try our new workflow]''' if you have not already. Our dashboard automatically [https://mdwiki.toolforge.org/Translation_Dashboard/leaderboard.php collects statistics] of your efforts and we are working on [https://mdwiki.toolforge.org/fixwikirefs.php tools to automatically improve formating]. |} Thanks again :-) -- [[mdwiki:User:Doc_James|<span style="color:#0000f1">'''Doc James'''</span>]] along with the rest of the team at '''[[m:WikiProject_Med|Wiki Project Med Foundation]]''' ೧೧:೫೪, ೨೬ ಜನವರಿ ೨೦೨೫ (IST) </div> <!-- Message sent by User:Doc James@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Top_Other_Language_Editors_2024&oldid=28172893 --> == Feminism and Folklore 2025: Important Updates for Organizers & Jury == Hello Community Organizers and Jury, Thank you for organising Feminism and Folklore writing competition on your wiki. Feminism and Folklore is the largest Wikipedia contest organized by community members. We congratulate you in joining and celebrating our cultural heritage and promoting gender equality on Wikipedia. To encourage boost for the contributions of the participants, we're offering prizes for Feminism and Folklore local prizes. Each Wikipedia will have three local winners: # First Prize: $25 USD # Second Prize: $20 USD # Best Jury Article: $15 USD All this will be in '''gift voucher format only'''. Prizes will only be given to users who have more than 5 accepted articles. No prizes will be given for users winning below 5 accepted articles. Kindly inform your local community regarding these prizes and post them on the local project page The Best Jury Article will be chosen by the jury based on how unique the article is aligned with the theme. The jury will review all submissions and decide the winner together, making sure it's fair. These articles will also be featured on our social media handles. We're also providing internet and childcare support to the first 75 organizers and Jury members for those who request for it. Remember, only 75 organizers will get this support, and it's given on a first-come, first-served basis. The registration form will close after 75 registrations, and the deadline is <nowiki>'''</nowiki>March 5, 2025<nowiki>'''</nowiki>. This support is optional and not compulsory, so if you're interested, fill out the [https://docs.google.com/forms/d/e/1FAIpQLSeum8md6FqHY1ISWRLW5bqOAv_lcd1tpVtMMZfWKRDU_IffLQ/viewform?usp=dialog Form] Each organizer/jury who gets support will receive $40 USD in gift voucher format, even if they're involved in more than one wiki. No dual support will be provided if you have signed up in more than one language. This support is meant to appreciate your volunteer support for the contest. We also invite all organizers and jury members to join us for Advocacy session on '''Saturday, Feb 28, 2025'''. This session will help you understand the jury process for both contests and give you a chance to ask questions. More details are on [[meta:Event:Telling untold stories: How to document gendered narratives in Folklore on Wikipedia|Event:Telling untold stories: How to document gendered narratives in Folklore on Wikipedia - Meta]] Let's celebrate our different cultures and work towards gender equality on Wikipedia! Best regards, Stella and Tiven Wiki loves folklore international team [[User:SAgbley|SAgbley]] ([[User talk:SAgbley|talk]]) 04:39, 25 February 2025 (UTC) <!-- Message sent by User:Joris Darlington Quarshie@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Joris_Darlington_Quarshie/Community_Prizes&oldid=28309519 --> == Final reminder that the deadline for submitting your Feminism and Folklore 2025 results == <div style="border:8px maroon ridge;padding:6px;> Hello {{PAGENAME}} [[File:Feminism and Folklore logo.svg | right | frameless]] I hope this message finds you well. This is a final reminder that the deadline for submitting your Feminism and Folklore 2025 results on Meta has elapsed. For participant prizes to be processed, your community must submit the necessary information as soon as possible. Please use the following link to submit your results: [https://meta.wikimedia.org/wiki/Feminism_and_Folklore_2025/Results Submit your results] We have extended the {{font color||yellow|'''submission deadline to 12 June 2025'''.}} Kindly note that failure to submit by this date will result in your group’s participants not receiving any prizes from us. Thank you for your attention to this important matter and for your continued contributions to the Feminism and Folklore campaign. Warm regards,<br> Stella<br> On behalf of Feminism and Folklore Team<br> [[User:MediaWiki message delivery|MediaWiki message delivery]] ([[User talk:MediaWiki message delivery|<span class="signature-talk">{{int:Talkpagelinktext}}</span>]]) 12:39, 08 June 2025 (UTC) </div> <!-- Message sent by User:Joris Darlington Quarshie@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Template:FnF_2025_reminder_list&oldid=28842648 --> == Notice of expiration of your interface-admin right == <div dir="ltr">Hi, as part of [[:m:Special:MyLanguage/Global reminder bot|Global reminder bot]], this is an automated reminder to let you know that your permission "interface-admin" (ಅಂತರಮುಕ ನಿರ್ವಾಹಕೆರ್) will expire on 2025-11-29 15:03:18. Please renew this right if you would like to continue using it. <i>In other languages: [[:m:Special:MyLanguage/Global reminder bot/Messages/default|click here]]</i> [[ಬಳಕೆದಾರೆ:Leaderbot|Leaderbot]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Leaderbot|ಪಾತೆರ್ಲೆ]]) ೦೧:೧೩, ೨೩ ನವಂಬರ್ ೨೦೨೫ (IST)</div> == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Feminism and Folklore 2026 starts soon == <div style="border:8px maroon ridge;padding:6px;"> [[File:Feminism and Folklore 2026 logo.svg|centre|550px|frameless]] ::<div lang="en" dir="ltr" class="mw-content-ltr"> <div style="text-align: center; width: 100%;">''{{int:please-translate}}''</div> ;Invitation to Organize Feminism and Folklore 2026 Dear Wikimedian, We thank you for taking an initiative to organize the '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]''' writing competition on your local Wikipedia. The international campaign will run from '''1 February to 31 March 2026''' and aims to improve coverage of feminism, women’s histories, gender-related topics, and folk culture across Wikipedia projects. ;About the Campaign '''Feminism and Folklore''' is a global writing initiative that complements the '''[[:c:Commons:Wiki Loves Folklore 2026|Wiki Loves Folklore]]''' photography competition. While Wiki Loves Folklore focuses on visual documentation, this writing campaign addresses the '''gender gap on Wikipedia''' by improving encyclopedic content related to folk culture and marginalized voices. ;What Can Participants Write About? Communities can contribute by creating, expanding, or translating articles related to: * Folk festivals, rituals, and celebrations * Folk dances, music, and traditional performances * Women and queer figures in folklore * Women in mythology and oral traditions * Women warriors, witches, and witch-hunting narratives * Fairy tales, folk stories, and legends * Folk games, sports, and cultural practices Participants may work from curated article lists or generate new article suggestions using campaign tools. ;Checklist for Organizers Organizers are requested to complete the following steps to register their community: # Create a local project page on your wiki [[:m:Feminism and Folklore/Sample|(see sample)]] # Set up the campaign using the '''CampWiz''' tool # Prepare a local article list and clearly mention: #* Campaign timeline #* Local and international prizes # Request a site notice from local administrators [[:mr:Template:SN-FNF|(see sample)]] # Add your local project page and CampWiz link to the '''[[:m:Feminism and Folklore 2026/Project Page|Meta project page]]''' ;Campaign Tools The Wiki Loves Folklore Tech Team has introduced tools to support organizers and participants: * '''Article List Generator by Topic''' – Helps identify articles available on English Wikipedia but missing in your local language Wikipedia. The tool allows customized filters and provides downloadable article lists in CSV and wikitable formats. * '''CampWiz''' – Enables communities to manage writing campaigns effectively, including jury-based evaluation. This will be the third year CampWiz is officially used for Feminism and Folklore. Both tools are now available for use in the campaign. '''[https://tools.wikilovesfolklore.org/ Click here to access the tools]''' ;Learn More & Get Support For detailed information about rules, timelines, and prizes, please visit the '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026 project page]]'''. If you have any questions or need assistance, feel free to reach out via: * '''[[:m:Talk:Feminism and Folklore 2026/Project Page|Meta talk page]]''' * Email us using details on the contact page. ;Join Us in upcoming office hours We look forward to your presence in the upcoming office hours where the community and the international Team can interact with each other and know more about the contest. resister yourself for the [[:m:Event:Wiki Loves Folklore 2026 Office Hours|office hours event]]. Thank you and all the best, '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026 International Team]]''' ---- ''Stay connected:'' [[File:B&W Facebook icon.png|link=https://www.facebook.com/feminismandfolklore/|30x30px]]&nbsp; [[File:B&W Twitter icon.png|link=https://twitter.com/wikifolklore|30x30px]] </div></div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೦೦, ೧೮ ಜನವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=29948401 --> == Feminism and Folklore 2026 – Community Organisers & Jury == Hello {{PAGENAME}}!, Thank you for taking the lead in organising '''Feminism and Folklore 2026''' in your community. We truly appreciate your efforts! To ensure a smooth and successful campaign, please make sure you have: * Fully completed all details on the [[:m:Feminism and Folklore 2026/Project Page|Feminism and Folklore 2026 Project Page]]: * Started promoting the campaign within your community. * Requested a local administrator to place a '''sitenotice''' about the campaign so users are notified. * Used the '''[https://tools.wikilovesfolklore.org/fnf/ Article List Generator Tool]''' and shared the generated article lists with your community. === Internet & Childcare Support === Community organisers and jury members who require '''internet and childcare support''' (non-mandatory, opt-in, request-only support) should fill the support request form '''by 22 February 2026'''. '''[https://docs.google.com/forms/d/e/1FAIpQLSeutXEF1yTnJfExWUYPIf6SkhcnTgul07BeI-biqT4RE_vsrA/viewform Link to the form]''' Requests submitted after this date will not be entertained. === Important Participation Guidelines === * Minimum article size: '''3000 bytes and 300 words''' (final decision may be set by local organisers). * If your country is not listed on the Article list generator tool, please contact us. === Community Engagement === * Keep your community active and motivated throughout the campaign. * Share your achievements and notable articles with us so we can highlight them globally. * In the support form, please indicate if you would like a '''quick coordination call after the campaign'''. Let’s make '''Feminism and Folklore''', under the banner of '''#WeTogether''', help bridge the '''gender gap''' and '''folklore gap''' on Wikipedia worldwide. 🌍✊ Thank you for your collaboration! ''If someone from your community organisers or jury has missed this message feel free to share this message with them.'' Feminism and Folklore International Team. –[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೦:೪೭, ೧೬ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=30083330 --> == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Next Steps and Feedback meeting for Feminism and Folklore Organizers == <div style="border:8px maroon ridge; padding:6px;"> [[File:Feminism and Folklore 2026 logo.svg|center|550px|frameless]] <div lang="en" dir="ltr" class="mw-content-ltr" style="padding: 1em 2em;"> <div style="text-align: center; width: 100%;">''{{int:please-translate}}''</div> Dear Organizer, I hope this message finds you well. First and foremost, on behalf of the International Team I want to extend my gratitude to you for your efforts in organizing the '''Feminism and Folklore 2026''' campaign on your local Wikipedia. Your contribution has been instrumental in bridging the gender and folk gap on Wikipedia, and we truly appreciate your dedication to this important cause. As the campaign has ended I wanted to inform you about the next steps. It's time to commence the jury process using the CampWiz or Fountain tool where your campaign was hosted. Please ensure that you update the details of the jury, campaign links and the names of organizers accurately on the [[:m:Feminism and Folklore 2026/Project Page|sign-up page]]. Once the jury process is completed, kindly update only the top 3 winners details on the [[:m:Feminism and Folklore 2026/Results|results page]] accordingly. The deadline for jury submission of results is '''April 30, 2026'''. However, if you find that the number of articles is high and you require more time, please don't hesitate to inform us via email or on campaign Meta Wiki talk page. We are more than willing to approve an extension if needed. Should you encounter any issues with the tools, please feel free to reach out to us on Telegram for assistance. Your feedback and progress updates are crucial for us to improve the campaign and better understand your community's insights. Therefore, we kindly ask you to spare just an hour to collectively share your progress and achievements with us during our '''[[:m:Event:Feminism and Folklore 2026 Post-Campaign Office Hour|community feedback session]]'''. Your input will greatly assist us in making the campaign more meaningful and impactful. Thank you once again for your hard work and dedication to the Feminism and Folklore campaign. Your efforts are deeply appreciated, and we look forward to hearing from you soon. Warm regards, [[User:Tiven2240|Tiven2240]] on behalf of Feminism and Folklore International Team <nowiki>#WeTogether</nowiki> </div></div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೭:೨೭, ೧೧ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/fnf26&oldid=30391231 --> == Last Few Days: WikiConference India 2026 Scholarship Applications == <div lang="en" dir="ltr" class="mw-content-ltr"> ''{{int:please-translate}}'' Dear Wikimedian, We're happy to share that scholarship applications for '''WikiConference India 2026''' are currently open and the deadline is just around the corner. [[m:Special:MyLanguage/WikiConference India 2026|WikiConference India 2026]] is the fourth edition of the national-level conference that brings together Wikimedians and stakeholders engaged in Indic-language Wikimedia projects and the broader open knowledge movement across India and South Asia. The conference will take place in Kochi, Kerala, from 4–6 September 2026. * You can find the more information and the application form at the [[m:Special:MyLanguage/WikiConference India 2026/Scholarship|Scholarship page here at Meta wiki]] * '''Scholarship deadline: 15 April 2026, 11:59 PM IST''' With only a few days left, we warmly encourage you to apply if you haven’t already and kindly request you to share this with your community and encourage others to apply. For more information and regular updates, we encourage you to visit the conference Meta page. Warm regards, <br> on behalf of the WikiConference India 2026 Organising Team ''This message was sent with [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) on ೦೦:೦೦, ೧೨ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST)'' </div> <!-- Message sent by User:Gnoeee@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/WCI_2026_active_users&oldid=30389801 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == Final Reminder: Submission of Local Winners for Feminism and Folklore 2026 == Dear Feminism and Folklore organiser, This is a friendly reminder that the deadline for submitting the names and details of your local winners for '''Feminism and Folklore 2026''' is '''5 June 2026'''. Please ensure that your community's [[:m:Feminism and Folklore 2026/Results|winner information]] is submitted before this date. Communities that do not provide their winner data by the deadline will unfortunately not be eligible for prize distribution, and the international organizing team will not be able to accommodate late submissions received after 5 June 2026. Thank you for your prompt attention to this matter and for your participation in the campaign. Best regards, '''Feminism and Folklore International Organizing Team''' --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೦೭:೩೯, ೩ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30627457 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Thank you for organising Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your dedication, coordination, and hard work played a vital role in making this year's [[:m:Feminism and Folklore 2026|Feminism and Folklore]] campaign successful. We truly appreciate the time and effort you invested in engaging your community and supporting participants. As we have completed the jury phase and begin planning for next year's campaign, we would like to learn from your experiences. Please complete the organisers' feedback form and share your insights, challenges, and suggestions on how we can improve the campaign. '''This feedback form is mandatory for all organisers''', as it helps us evaluate the campaign and make meaningful improvements for future editions. '''Form link [https://docs.google.com/forms/d/e/1FAIpQLSf6IsGAsxkTEWTwueyLg-gBjRVlY3g2kTNj3xRhDiILiQnQwQ/viewform?usp=dialog here]''' Please submit your response by '''25 June 2026'''. Thank you once again for your invaluable contributions and for helping make Feminism and Folklore a global success. We look forward to working with you again in the future! Regards, '''Feminism and Folklore Team''' --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೭:೩೫, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704175 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == [[Confédération Mondiale des Activités Subaquatiques]] == Hi, I noticed that you imported the article “Confédération Mondiale des Activités Subaquatiques” in May, but the text is still in English. Could you please take another look at this when you have a chance? Thanks! [[ಬಳಕೆದಾರೆ:Icodense|Icodense]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Icodense|ಪಾತೆರ್ಲೆ]]) ೧೫:೪೮, ೧೨ ಜುಲಾಯಿ ೨೦೨೬ (IST) PS: Same for [[The Partisan]]. == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == ಟೆಂಪ್ಲೇಟ್‌ದ ಬಗೆಟ್ == ಫತೇಪುರ್ ಸಿಕ್ರಿ ಲೇಕನೊಡು ಅರ್ತ ಆವಂದಿ ವಾಕ್ಯೊಲು ಸರಿ ಮಲ್ತ್‌ದ್ ಸರಿ ಮಲ್ಪೆರೆ ಬರೆತಾರ್, ದಯಮಲ್ತ್‌ದ್ ಅರ್ತ ಆವಂದಿನ ವಾಕ್ಯ ಒವ್ವು ಪಂಡ್ದ್ ತೋಜಪಾಲೆ ಸರಿ ಮಲ್ಪುಗ. ದನ್ಯವಾದೊಲು. [[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೧:೧೩, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :@[[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]]: [[ಫತೇಪುರ್ ಸಿಕ್ರಿ]] [[Special:Diff/366101]] ಕೆಲವೊಂಜೆನ್ ಸರಿ ಮಲ್ತ್‌ದ; ಲೇಕನೊ ಇತ್ತೆ ಸರಿಯಾದ್ ಇತ್ತ್ಂಡ, ಏರೆಗ್ಲಾ ಈ <code><nowiki>{{Copy edit}}</nowiki></code> ನಿರ್ವಹಣೆದ ಟೆಂಪ್ಲೇಟ್‌ನ್ ದೆತ್ತ್ ಪಾಡ್‌ದ್ ಕೊಡುಗೆ ಕೊರೊಲಿ. ಇರೆಗ್ ಸೊಲ್ಮೆಲು, [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೩:೨೧, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::ಅಭಿಯಾನೊಗು ಕಾರಣವಾದ್ ಅಕ್ಬರ್ ಉಂದೆನ್ ಬುಡ್ದು ಪೋಯೆರ್, ಬೊಕ್ಕ 1610 ಡ್ದ್ ಉಂದು ಪೂರ್ತಿಯಾದ್ ಕೈಬುಡಿಯೆರ್.[೨] - ನೆಟ್ಟ್ ಅಭಿಯಾನೊ ಲಾ ಕನ್ನಡ ಪ್ರಚಾರ ಲಾ ಕನ್ನಡ. ಪೂರ್ತಿಯಾದ್ ಲಾ ಕನ್ನಡ, ಗಮನೊನು ಲಾ ಕನ್ನಡ ಅವೆರ್ದ್ ಗುಮನೊನು ಅತು. ಬೇತೆ ವೊವ್ ವಾಕ್ಯ ಪೂರ್ತಿಯಾದ್ ಇಜ್ಜಿ ಪಂಡ್ದ್ ತೆರಿಪಾಂಡ ಬಾರೀ ಎಡ್ಡೆ ಇತ್ತುಂಡು. '''ಅಂಡಮಾನ್ ಬೊಕ್ಕ ನಿಕೋಬಾರ್ ದ್ವೀಪೊಲು''' ನೆಟ್ಟ್ ನನಾತ್ ಬರೆಯೆರೆ ಉಂಡು. ಅವು ಬುಡ್ದು ಪೊಸ ಲೇಕನೊಗ್ ದಾಯೆ ಪೋವೊಡು. ಆಯಿಕ್ ಬೋಡಾಪಿನ ವಿಸಯೊಲು ಉಲ್ಲ, ಅವೆನ್ ಮುಗಿಪಂದೆ ಪೊಸ ಲೇಕನ ದಾಯೆ ಬರೆಪುನಿ. ದುಂಬುದೆನ್ ಎರ್ ಮುಗಿಪೆರೆ. ಆಯಿಕ್ ಟೆಂಪ್ಲೇಟ್ ದಾಯೆ ಪಾಡಿಜಾರ್. ಬೊಕ್ಕ ಬರೆಪೆ ಪಂಡ್ದ್ ಬರೆತಿಜೆರ್. ನಮ ಪೂರಾ {{under construction}} ಪಂಡ್ದ್ ಪಾಡೊಂದಿತ್ತ. ವಂತೆ ಅಲ್ಪಲ ಗಮನ ಕೊರ್ಲೆ. ಇರೆನ ಉತ್ತರೊಗು ಸೊಲ್ಮೆಲು. [[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೩:೪೩, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) 7wxvmriictjrf7fi2bvydtfzkq4x8af ಮೋಡ್ಯೂಲ್:Transclusion count/data/C 828 10244 366134 363625 2026-08-16T05:07:10Z en>Ahechtbot 0 [[Wikipedia:BOT|Bot]]: Updated page. 366134 Scribunto text/plain return { ["C"] = 800000, ["C-Class"] = 17000, ["C-SPAN"] = 13000, ["C-cmn"] = 2800, ["C-pl"] = 51000, ["C."] = 352000, ["CAN"] = 21000, ["CANelec"] = 18000, ["CANelec/gain"] = 3500, ["CANelec/hold"] = 6600, ["CANelec/source"] = 11000, ["CANelec/top"] = 8900, ["CANelec/total"] = 9400, ["CAS"] = 3800, ["CAS_Registry_Number"] = 3800, ["CBB_Yearly_Record_End"] = 3500, ["CBB_Yearly_Record_Entry"] = 3600, ["CBB_Yearly_Record_Start"] = 3400, ["CBB_Yearly_Record_Subhead"] = 4200, ["CBB_Yearly_Record_Subtotal"] = 3300, ["CBB_roster/Footer"] = 11000, ["CBB_roster/Header"] = 11000, ["CBB_roster/Player"] = 11000, ["CBB_schedule_end"] = 14000, ["CBB_schedule_entry"] = 14000, ["CBB_schedule_start"] = 14000, ["CBB_standings_end"] = 19000, ["CBB_standings_entry"] = 19000, ["CBB_standings_start"] = 19000, ["CBB_yearly_record_end"] = 4700, ["CBB_yearly_record_end/legend"] = 4100, ["CBB_yearly_record_entry"] = 4600, ["CBB_yearly_record_start"] = 4500, ["CBB_yearly_record_subhead"] = 4300, ["CBB_yearly_record_subtotal"] = 4400, ["CBSB_Standings_End"] = 2700, ["CBSB_Standings_Entry"] = 2700, ["CBSB_Standings_Start"] = 2600, ["CBSB_link"] = 4100, ["CBSB_standings_end"] = 5500, ["CBSB_standings_entry"] = 5500, ["CBSB_standings_start"] = 5500, ["CC0"] = 4000, ["CC_BY-SA_2.5"] = 2100, ["CC_BY-SA_2.5,_2.0,_1.0"] = 2100, ["CC_BY-SA_3.0"] = 21000, ["CC_BY-SA_3.0_migrated"] = 19000, ["CC_BY-SA_4.0"] = 14000, ["CC_BY_2.5"] = 2600, ["CC_BY_3.0"] = 7000, ["CFB_Standings_End"] = 16000, ["CFB_Standings_Entry"] = 16000, ["CFB_Standings_Start"] = 16000, ["CFB_Yearly_Record_End"] = 7900, ["CFB_Yearly_Record_End/legend"] = 2900, ["CFB_Yearly_Record_Entry"] = 7900, ["CFB_Yearly_Record_Start"] = 7900, ["CFB_Yearly_Record_Subhead"] = 8000, ["CFB_Yearly_Record_Subtotal"] = 7800, ["CFB_schedule"] = 29000, ["CFB_schedule_entry"] = 22000, ["CFB_standings_end"] = 39000, ["CFB_standings_entry"] = 39000, ["CFB_standings_start"] = 39000, ["CFL_Year"] = 7200, ["CGF_year"] = 3800, ["CGNDB_URL"] = 5400, ["CHE"] = 12000, ["CHI"] = 3300, ["CHL"] = 4300, ["CHN"] = 15000, ["CN"] = 4000, ["CO2"] = 3500, ["COI"] = 14000, ["COIUL"] = 131000, ["COI_editnotice"] = 7800, ["COL"] = 5600, ["COLON"] = 14000, ["CRI"] = 2300, ["CRO"] = 6600, ["CS1_config"] = 15000, ["CSK"] = 2800, ["CSS_crop"] = 2100, ["CSS_image_crop"] = 9500, ["CUB"] = 4200, ["CURRENTDATE"] = 3600, ["CYP"] = 2200, ["CZE"] = 16000, ["Calendar"] = 2500, ["Calendar/styles.css"] = 2800, ["California/color"] = 12000, ["Call_sign_disambiguation"] = 3100, ["Campaignbox"] = 26000, ["CanProvName"] = 19000, ["CanadaByProvinceCatNav"] = 9500, ["CanadaProvinceThe"] = 4500, ["Canada_NTS_Map_Sheet"] = 2100, ["Canada_by_province_category_navigation"] = 9900, ["Canadian_English"] = 13000, ["Canadian_Parliament_links"] = 5100, ["Canadian_election_result"] = 18000, ["Canadian_election_result/gain"] = 3600, ["Canadian_election_result/hold"] = 6900, ["Canadian_election_result/source"] = 12000, ["Canadian_election_result/top"] = 18000, ["Canadian_election_result/total"] = 14000, ["Canadian_party_colour"] = 11000, ["Canadian_party_colour/colour"] = 23000, ["Canadian_party_colour/colour/default"] = 22000, ["Canadian_party_colour/name"] = 19000, ["Canadian_party_colour/name/default"] = 8300, ["Canadian_province_name"] = 19000, ["Canned_search"] = 8500, ["Cascite"] = 17000, ["Caselaw_source"] = 4800, ["Cassini-Ehess"] = 3100, ["Cast_listing"] = 29000, ["Castlist"] = 5600, ["Cat"] = 471000, ["CatAutoTOC"] = 373000, ["CatRel"] = 4100, ["CatTrack"] = 20000, ["Cat_class"] = 6300, ["Cat_main"] = 149000, ["Cat_more"] = 102000, ["Cat_more_if_exists"] = 29000, ["Cat_see_also"] = 3800, ["Catalog_lookup_link"] = 60000, ["Catalog_of_Fishes"] = 9700, ["Catalogue_of_Life"] = 24000, ["Category"] = 2400, ["Category-Class"] = 14000, ["Category-inline"] = 11000, ["Category_TOC"] = 80000, ["Category_TOC/tracking"] = 36000, ["Category_U.S._State_elections_by_year"] = 7500, ["Category_U.S._State_elections_by_year/core"] = 7500, ["Category_class"] = 25000, ["Category_described_in_year"] = 6400, ["Category_diffuse"] = 11000, ["Category_disambiguation"] = 2700, ["Category_explanation"] = 415000, ["Category_handler"] = 2720000, ["Category_if_exists"] = 94000, ["Category_ifexist"] = 4900, ["Category_importance"] = 8800, ["Category_link"] = 691000, ["Category_link/core"] = 691000, ["Category_link_if_exists"] = 81000, ["Category_link_with_count"] = 9200, ["Category_link_without_namespace"] = 801000, ["Category_links"] = 9900, ["Category_main_article"] = 221000, ["Category_more"] = 143000, ["Category_more_if_exists"] = 49000, ["Category_ordered_by_date"] = 13000, ["Category_other"] = 1110000, ["Category_pair"] = 2200, ["Category_redirect"] = 159000, ["Category_redirect/core"] = 159000, ["Category_see_also"] = 55000, ["Category_see_also/Category_pair_check"] = 55000, ["Category_see_also_if_exists"] = 95000, ["Category_see_also_if_exists_2"] = 101000, ["Category_series_navigation"] = 598000, ["Category_title"] = 2400, ["Catexp"] = 5900, ["CathEncy"] = 2300, ["Catholic"] = 3700, ["Catholic_Encyclopedia"] = 5100, ["Catmain"] = 29000, ["Catmore"] = 9800, ["Catseealso"] = 2300, ["Cbb_link"] = 10000, ["Cbignore"] = 132000, ["Cbsb_link"] = 2500, ["Cc-by-2.5"] = 2600, ["Cc-by-3.0"] = 7000, ["Cc-by-sa-2.5"] = 2100, ["Cc-by-sa-2.5,2.0,1.0"] = 2100, ["Cc-by-sa-3.0"] = 20000, ["Cc-by-sa-3.0-migrated"] = 19000, ["Cc-by-sa-4.0"] = 13000, ["Cc-zero"] = 3800, ["CensusAU"] = 9100, ["Census_2016_AUS"] = 5700, ["Census_2021_AUS"] = 6200, ["Cent"] = 6900, ["Center"] = 301000, ["Centralized_discussion"] = 7200, ["Centralized_discussion/core"] = 7200, ["Centralized_discussion/styles.css"] = 7200, ["Centre"] = 4200, ["Century"] = 16000, ["Century_name_from_decade"] = 2400, ["Century_name_from_decade_or_year"] = 127000, ["Century_name_from_title_decade"] = 7600, ["Century_name_from_title_year"] = 9200, ["Certification_Cite_Ref"] = 38000, ["Certification_Table_Bottom"] = 37000, ["Certification_Table_Entry"] = 39000, ["Certification_Table_Entry/Foot"] = 37000, ["Certification_Table_Entry/Foot/helper"] = 37000, ["Certification_Table_Entry/MexicanAward"] = 2300, ["Certification_Table_Entry/Region"] = 38000, ["Certification_Table_Entry/Sales"] = 37000, ["Certification_Table_Entry/Sales/AustrianPeriod"] = 2500, ["Certification_Table_Entry/Sales/BelgianPeriod"] = 2400, ["Certification_Table_Entry/Sales/DanishPeriod"] = 4400, ["Certification_Table_Entry/Sales/DanishPeriodHelper1"] = 4400, ["Certification_Table_Entry/Sales/DanishPeriodHelper2"] = 4400, ["Certification_Table_Entry/Sales/FrenchPeriod"] = 2200, ["Certification_Table_Entry/Sales/FrenchPeriodAlbum"] = 2000, ["Certification_Table_Entry/Sales/GermanPeriod"] = 4100, ["Certification_Table_Entry/Sales/ItalianHelper"] = 4500, ["Certification_Table_Entry/Sales/MexicanSales"] = 2200, ["Certification_Table_Entry/Sales/NewZealandPeriod"] = 7800, ["Certification_Table_Entry/Sales/SpanishSinglesPeriod"] = 2800, ["Certification_Table_Entry/Sales/SwedishPeriod"] = 2300, ["Certification_Table_Separator"] = 3300, ["Certification_Table_Top"] = 39000, ["Cfb_link"] = 26000, ["Cfd_all"] = 6100, ["Cfm_full"] = 2400, ["Cfr_full"] = 3500, ["Chart_bottom"] = 4500, ["Chart_top"] = 4500, ["Check_completeness_of_transclusions"] = 64000, ["Check_talk"] = 63000, ["Check_talk_wp"] = 1460000, ["Check_winner_by_scores"] = 16000, ["CheckedSockpuppet"] = 7200, ["Checked_sockpuppet"] = 18000, ["Checkedsockpuppet"] = 5300, ["Checkip"] = 12000, ["Checkuser"] = 90000, ["Checkuser/Hacky_user_info_card_wrapper_template_(do_not_use_outside_of_modules)"] = 53000, ["Checkuserblock-account"] = 44000, ["Chem"] = 6100, ["Chem/atom"] = 6000, ["Chem/link"] = 6100, ["Chem2"] = 11000, ["Chem_molar_mass"] = 22000, ["Chem_molar_mass/format"] = 22000, ["Chembox"] = 16000, ["Chembox/styles.css"] = 16000, ["Chembox_3DMet"] = 15000, ["Chembox_AllOtherNames"] = 14000, ["Chembox_AllOtherNames/format"] = 14000, ["Chembox_Appearance"] = 7200, ["Chembox_BoilingPt"] = 4200, ["Chembox_CASNo"] = 15000, ["Chembox_CASNo/format"] = 15000, ["Chembox_CalcTemperatures"] = 7600, ["Chembox_ChEBI"] = 15000, ["Chembox_ChEBI/format"] = 15000, ["Chembox_ChEMBL"] = 15000, ["Chembox_ChEMBL/format"] = 15000, ["Chembox_ChemSpiderID"] = 15000, ["Chembox_ChemSpiderID/format"] = 15000, ["Chembox_CompTox"] = 15000, ["Chembox_CompTox/format"] = 15000, ["Chembox_Datapage_check"] = 16000, ["Chembox_Density"] = 5700, ["Chembox_DrugBank"] = 15000, ["Chembox_DrugBank/format"] = 15000, ["Chembox_ECHA"] = 8300, ["Chembox_ECNumber"] = 15000, ["Chembox_ECNumber/format"] = 15000, ["Chembox_Elements"] = 15000, ["Chembox_Elements/molecular_formula"] = 21000, ["Chembox_FlashPt"] = 2100, ["Chembox_Footer"] = 15000, ["Chembox_Footer/tracking"] = 16000, ["Chembox_GHS_(set)"] = 4500, ["Chembox_Hazards"] = 13000, ["Chembox_IUPHAR_ligand"] = 15000, ["Chembox_IUPHAR_ligand/format"] = 15000, ["Chembox_Identifiers"] = 15000, ["Chembox_InChI"] = 15000, ["Chembox_InChI/format"] = 15000, ["Chembox_Indexlist"] = 15000, ["Chembox_Jmol"] = 15000, ["Chembox_Jmol/format"] = 15000, ["Chembox_KEGG"] = 15000, ["Chembox_KEGG/format"] = 15000, ["Chembox_MeltingPt"] = 6600, ["Chembox_OtherCpds"] = 2100, ["Chembox_Properties"] = 15000, ["Chembox_PubChem"] = 15000, ["Chembox_PubChem/format"] = 15000, ["Chembox_RTECS"] = 15000, ["Chembox_RTECS/format"] = 15000, ["Chembox_Related"] = 4200, ["Chembox_SMILES"] = 15000, ["Chembox_SMILES/format"] = 15000, ["Chembox_SolubilityInWater"] = 4500, ["Chembox_Structure"] = 2400, ["Chembox_UNII"] = 15000, ["Chembox_UNII/format"] = 15000, ["Chembox_headerbar"] = 16000, ["Chembox_image"] = 14000, ["Chembox_image_cell"] = 14000, ["Chembox_image_sbs"] = 14000, ["Chembox_parametercheck"] = 15000, ["Chembox_setDatarow"] = 5500, ["Chembox_setHeader"] = 5500, ["Chembox_templatePar/formatPreviewMessage"] = 16000, ["Chembox_verification"] = 6900, ["Chemspidercite"] = 11000, ["Chessgames_player"] = 3800, ["Chinese"] = 3500, ["Circa"] = 469000, ["Circular_reference"] = 3800, ["Citation"] = 412000, ["Citation/make_link"] = 7900, ["Citation/styles.css"] = 53000, ["Citation_needed"] = 636000, ["Citation_needed_span"] = 3600, ["Citation_style"] = 4500, ["Cite_AV_media"] = 80000, ["Cite_AV_media_notes"] = 34000, ["Cite_Appletons'"] = 2400, ["Cite_Australian_Dictionary_of_Biography"] = 4900, ["Cite_Box_Office_Mojo"] = 2500, ["Cite_Catholic_Encyclopedia"] = 8600, ["Cite_DCB"] = 3300, ["Cite_DGRBM"] = 2200, ["Cite_DNB"] = 20000, ["Cite_EB1911"] = 26000, ["Cite_FTP"] = 2500, ["Cite_GBIF"] = 4300, ["Cite_GNIS"] = 2400, ["Cite_Gaia_DR3"] = 3100, ["Cite_Google_Maps"] = 20000, ["Cite_Hansard"] = 2700, ["Cite_IUCN"] = 71000, ["Cite_Instagram"] = 7400, ["Cite_Jewish_Encyclopedia"] = 3200, ["Cite_LCDB"] = 2000, ["Cite_Metacritic"] = 2300, ["Cite_NIE"] = 3500, ["Cite_NSW_Parliament"] = 3300, ["Cite_NSW_SHR"] = 2700, ["Cite_NatureServe"] = 2900, ["Cite_ODNB"] = 22000, ["Cite_OED"] = 2500, ["Cite_POWO"] = 6300, ["Cite_Q"] = 17000, ["Cite_QHR"] = 3100, ["Cite_QPN"] = 4100, ["Cite_Rotten_Tomatoes"] = 2900, ["Cite_Rowlett"] = 2500, ["Cite_Russian_law"] = 7800, ["Cite_Ryan"] = 3300, ["Cite_Sports-Reference"] = 55000, ["Cite_USGov"] = 13000, ["Cite_WoRMS"] = 13000, ["Cite_act"] = 4000, ["Cite_arXiv"] = 7100, ["Cite_bcgnis"] = 3700, ["Cite_book"] = 1910000, ["Cite_certification"] = 43000, ["Cite_certification/Title"] = 39000, ["Cite_certification/URL"] = 43000, ["Cite_certification/archivedate"] = 7600, ["Cite_certification/archiveurl"] = 7600, ["Cite_cgndb"] = 5400, ["Cite_comic"] = 2100, ["Cite_conference"] = 22000, ["Cite_court"] = 6800, ["Cite_dictionary"] = 3600, ["Cite_document"] = 2000, ["Cite_encyclopedia"] = 243000, ["Cite_episode"] = 19000, ["Cite_gnis"] = 35000, ["Cite_instagram"] = 3100, ["Cite_interview"] = 11000, ["Cite_iucn"] = 70000, ["Cite_journal"] = 1180000, ["Cite_legislation_UK"] = 4000, ["Cite_magazine"] = 381000, ["Cite_map"] = 60000, ["Cite_news"] = 1930000, ["Cite_newspaper_The_Times"] = 7200, ["Cite_patent"] = 7300, ["Cite_patent/authors"] = 6100, ["Cite_patent/core"] = 7600, ["Cite_peakbagger"] = 5300, ["Cite_podcast"] = 6200, ["Cite_press_release"] = 86000, ["Cite_report"] = 76000, ["Cite_rowlett"] = 2500, ["Cite_ship_register"] = 2000, ["Cite_simbad"] = 5600, ["Cite_sports-reference"] = 60000, ["Cite_taxon"] = 5500, ["Cite_tech_report"] = 3100, ["Cite_thesis"] = 50000, ["Cite_tweet"] = 48000, ["Cite_video"] = 13000, ["Cite_video_game"] = 3400, ["Cite_web"] = 5420000, ["Cite_wikisource"] = 7100, ["Cite_wikisource/make_link"] = 65000, ["Citeweb"] = 4100, ["Civil_navigation"] = 2100, ["Cjersey"] = 3000, ["Cl"] = 218000, ["Clade"] = 10000, ["Clade/styles.css"] = 10000, ["Clarification_needed"] = 2600, ["Clarify"] = 45000, ["Class"] = 39000, ["Class/colour"] = 19000, ["Class/icon"] = 21000, ["Class_mask"] = 461000, ["Classicon"] = 5200, ["Clc"] = 7400, ["Cleanup"] = 8400, ["Cleanup_reorganize"] = 2300, ["Cleanup_rewrite"] = 6800, ["Clear"] = 3750000, ["Clear_left"] = 26000, ["Clear_right"] = 2800, ["Clerk_Request"] = 2500, ["Clerk_note"] = 11000, ["Clerknote"] = 8600, ["Clickable_button"] = 1870000, ["Clickable_button_2"] = 227000, ["Closed_access"] = 5600, ["Closed_peer_review_page"] = 3200, ["Closed_rfc_bottom"] = 2200, ["Closed_rfc_top"] = 2900, ["Clr"] = 3100, ["Club_players_category"] = 9500, ["Clubplayerscat"] = 9500, ["Cmbox"] = 538000, ["Cn"] = 113000, ["Cnote2"] = 2900, ["Cnote2_Begin"] = 2900, ["Cnote2_End"] = 2900, ["Coat_of_arms"] = 2500, ["Cob"] = 15000, ["Code"] = 297000, ["Cof_family"] = 4500, ["Cof_genus"] = 5300, ["Col-1-of-2"] = 2400, ["Col-2"] = 186000, ["Col-2-of-2"] = 2300, ["Col-3"] = 11000, ["Col-4"] = 3800, ["Col-begin"] = 231000, ["Col-begin/styles.css"] = 231000, ["Col-break"] = 229000, ["Col-end"] = 231000, ["Col-float"] = 4900, ["Col-float-break"] = 4700, ["Col-float-end"] = 4800, ["Col-float/styles.css"] = 4900, ["Col-start"] = 9900, ["Col_div"] = 2200, ["Colbegin"] = 24000, ["Colend"] = 27000, ["Collapse"] = 8800, ["Collapse_bottom"] = 56000, ["Collapse_top"] = 57000, ["Collapse_top/styles.css"] = 57000, ["Collapsebottom"] = 4100, ["Collapsed_infobox_section_begin"] = 4400, ["Collapsed_infobox_section_begin/cont"] = 4400, ["Collapsed_infobox_section_begin/styles.css"] = 4400, ["Collapsed_infobox_section_end"] = 4400, ["Collapsetop"] = 4100, ["Collapsible_list"] = 68000, ["Collapsible_option"] = 151000, ["Colledge:_Ships_RN"] = 3000, ["College"] = 12000, ["CollegePrimaryHeader"] = 8200, ["CollegePrimaryStyle"] = 111000, ["CollegeSecondaryStyle"] = 11000, ["College_Athlete_Recruit_End"] = 2800, ["College_Athlete_Recruit_Entry"] = 2900, ["College_Athlete_Recruit_Start"] = 2900, ["College_athlete_recruit_end"] = 4900, ["College_athlete_recruit_entry"] = 5200, ["College_athlete_recruit_start"] = 5200, ["College_color_list"] = 4400, ["College_sports_team_link"] = 31000, ["Colon"] = 20000, ["Color"] = 501000, ["Color/styles.css"] = 501000, ["Color_box"] = 86000, ["Colorbox"] = 2600, ["Colorbull"] = 4600, ["Colored_link"] = 106000, ["Coloredlink"] = 2700, ["Colors"] = 3600, ["Colour"] = 5500, ["Colour_box"] = 2500, ["Coloured_link"] = 12000, ["Column"] = 4300, ["Column/styles.css"] = 4300, ["Columns-end"] = 4000, ["Columns-list"] = 125000, ["Columns-start"] = 4000, ["Comic_Book_DB"] = 3500, ["Comicbookdb"] = 3400, ["Comics-replaceability"] = 2700, ["Comics_infobox_sec/formcat"] = 3400, ["Comics_infobox_sec/genre"] = 4200, ["Comics_infobox_sec/genrecat"] = 3800, ["Comics_infobox_sec/styles.css"] = 8100, ["Comma-separated_list"] = 3700, ["Comma-separated_list/styles.css"] = 3700, ["Comma_separated_entries"] = 522000, ["Comment"] = 7600, ["Committed_identity"] = 3200, ["Committed_identity/styles.css"] = 3200, ["Commons"] = 73000, ["Commons-inline"] = 25000, ["Commons_cat"] = 45000, ["Commons_category"] = 904000, ["Commons_category-inline"] = 177000, ["Commons_category_inline"] = 5700, ["Commonscat"] = 59000, ["Commonscat-inline"] = 14000, ["Commonscat_inline"] = 2200, ["Commonscatinline"] = 7200, ["Compact_TOC"] = 7300, ["Compact_ToC"] = 3200, ["Compose_molecular_formula"] = 2400, ["Composition_bar"] = 13000, ["Confirmed"] = 20000, ["Confused"] = 3200, ["Confusing"] = 2400, ["CongBio"] = 9400, ["CongLinks"] = 4800, ["Connected_contributor"] = 21000, ["Connected_contributor_(paid)"] = 10000, ["Constellation_navbox"] = 9900, ["Container"] = 14000, ["Container_cat"] = 9100, ["Container_category"] = 92000, ["Containercat"] = 2800, ["Contains_special_characters"] = 5300, ["Contains_special_characters/core"] = 5300, ["Contains_special_characters/styles.css"] = 5300, ["Content_category"] = 7900, ["Contentious_topics/Additional_restrictions.json"] = 21000, ["Contentious_topics/Arab-Israeli_editnotice"] = 6900, ["Contentious_topics/Arab-Israeli_talk_notice"] = 6100, ["Contentious_topics/Category_database.json"] = 21000, ["Contentious_topics/Restrictions_definition.json"] = 21000, ["Contentious_topics/Standard_set.json"] = 21000, ["Contentious_topics/list"] = 29000, ["Contentious_topics/page_restriction_editnotice_base"] = 7400, ["Contentious_topics/page_restriction_talk_notice"] = 2700, ["Contentious_topics/talk_notice"] = 21000, ["Context"] = 2600, ["Continent2continental"] = 45000, ["Continent_adjective_to_noun"] = 10000, ["Continental2continent"] = 22000, ["Controversial"] = 3000, ["Convert"] = 1420000, ["Convert_abbreviated"] = 171000, ["Convinfobox"] = 308000, ["Coord"] = 1400000, ["Coord_missing"] = 70000, ["Coord_missing/CheckCat"] = 69000, ["Coords"] = 6600, ["Copied"] = 22000, ["Copy_edit"] = 2100, ["Copy_to_Commons"] = 2200, ["Copy_to_Wikimedia_Commons"] = 85000, ["Copy_to_Wikimedia_Commons_in"] = 4300, ["Copyvios"] = 2000, ["Cospar"] = 2800, ["Cot"] = 14000, ["Count"] = 979000, ["Country2continent"] = 89000, ["Country2continental"] = 31000, ["Country2nationality"] = 35000, ["CountryPrefixThe"] = 78000, ["Country_abbreviation"] = 95000, ["Country_alias"] = 17000, ["Country_at_games_navbox"] = 5800, ["Country_at_games_navbox/below"] = 5800, ["Country_data"] = 12000, ["Country_data_AFG"] = 2600, ["Country_data_ALB"] = 7300, ["Country_data_ALG"] = 11000, ["Country_data_AND"] = 3700, ["Country_data_ANG"] = 4400, ["Country_data_ARG"] = 53000, ["Country_data_ARM"] = 8300, ["Country_data_AUS"] = 89000, ["Country_data_AUT"] = 52000, ["Country_data_AZE"] = 11000, ["Country_data_Afghanistan"] = 21000, ["Country_data_Alabama"] = 2600, ["Country_data_Alaska"] = 2100, ["Country_data_Albania"] = 35000, ["Country_data_Alberta"] = 3300, ["Country_data_Algeria"] = 47000, ["Country_data_American_Samoa"] = 2900, ["Country_data_Andorra"] = 9300, ["Country_data_Angola"] = 21000, ["Country_data_Anguilla"] = 2400, ["Country_data_Antigua_and_Barbuda"] = 6400, ["Country_data_Apulia"] = 7900, ["Country_data_Argentina"] = 150000, ["Country_data_Arizona"] = 2500, ["Country_data_Arkansas"] = 2800, ["Country_data_Armenia"] = 46000, ["Country_data_Aruba"] = 4000, ["Country_data_Australia"] = 274000, ["Country_data_Austria"] = 144000, ["Country_data_Austria-Hungary"] = 2300, ["Country_data_Azerbaijan"] = 51000, ["Country_data_BAH"] = 4700, ["Country_data_BAN"] = 4500, ["Country_data_BAR"] = 2700, ["Country_data_BDI"] = 2000, ["Country_data_BEL"] = 60000, ["Country_data_BEN"] = 2000, ["Country_data_BER"] = 2500, ["Country_data_BFA"] = 2000, ["Country_data_BHR"] = 5800, ["Country_data_BIH"] = 14000, ["Country_data_BLR"] = 26000, ["Country_data_BOL"] = 6600, ["Country_data_BOT"] = 3100, ["Country_data_BRA"] = 65000, ["Country_data_BUL"] = 31000, ["Country_data_Bahamas"] = 11000, ["Country_data_Bahrain"] = 21000, ["Country_data_Bangladesh"] = 34000, ["Country_data_Barbados"] = 8800, ["Country_data_Belarus"] = 89000, ["Country_data_Belgium"] = 155000, ["Country_data_Belize"] = 5100, ["Country_data_Benin"] = 8100, ["Country_data_Bermuda"] = 6400, ["Country_data_Bhutan"] = 4900, ["Country_data_Bolivia"] = 24000, ["Country_data_Bosnia_and_Herzegovina"] = 53000, ["Country_data_Botswana"] = 10000, ["Country_data_Brazil"] = 169000, ["Country_data_British_Columbia"] = 3700, ["Country_data_British_Empire"] = 2100, ["Country_data_British_Raj"] = 2500, ["Country_data_British_Virgin_Islands"] = 3400, ["Country_data_Brunei"] = 6900, ["Country_data_Bulgaria"] = 100000, ["Country_data_Burkina_Faso"] = 11000, ["Country_data_Burma"] = 2300, ["Country_data_Burundi"] = 6800, ["Country_data_CAM"] = 2500, ["Country_data_CAN"] = 69000, ["Country_data_CGO"] = 2500, ["Country_data_CHE"] = 5900, ["Country_data_CHI"] = 20000, ["Country_data_CHL"] = 2400, ["Country_data_CHN"] = 49000, ["Country_data_CIV"] = 8600, ["Country_data_CMR"] = 8800, ["Country_data_COD"] = 3500, ["Country_data_COL"] = 28000, ["Country_data_CPV"] = 2300, ["Country_data_CRC"] = 7000, ["Country_data_CRO"] = 37000, ["Country_data_CUB"] = 13000, ["Country_data_CYP"] = 11000, ["Country_data_CZE"] = 54000, ["Country_data_California"] = 6300, ["Country_data_Cambodia"] = 9900, ["Country_data_Cameroon"] = 31000, ["Country_data_Canada"] = 260000, ["Country_data_Cape_Verde"] = 7100, ["Country_data_Castile_and_León"] = 2000, ["Country_data_Catalonia"] = 3400, ["Country_data_Cayman_Islands"] = 4600, ["Country_data_Central_African_Republic"] = 5200, ["Country_data_Chad"] = 5500, ["Country_data_Chile"] = 90000, ["Country_data_China"] = 149000, ["Country_data_Chinese_Taipei"] = 39000, ["Country_data_Colombia"] = 95000, ["Country_data_Colorado"] = 5300, ["Country_data_Commonwealth_of_Independent_States"] = 2200, ["Country_data_Comoros"] = 4700, ["Country_data_Confederate_States_of_America"] = 3100, ["Country_data_Connecticut"] = 3400, ["Country_data_Cook_Islands"] = 3900, ["Country_data_Costa_Rica"] = 33000, ["Country_data_Croatia"] = 104000, ["Country_data_Cuba"] = 46000, ["Country_data_Curaçao"] = 4400, ["Country_data_Cyprus"] = 46000, ["Country_data_Czech_Republic"] = 151000, ["Country_data_Czechoslovakia"] = 34000, ["Country_data_DEN"] = 40000, ["Country_data_DEU"] = 11000, ["Country_data_DNK"] = 4400, ["Country_data_DOM"] = 8400, ["Country_data_DRC"] = 2100, ["Country_data_Delaware"] = 2700, ["Country_data_Democratic_Republic_of_the_Congo"] = 22000, ["Country_data_Denmark"] = 120000, ["Country_data_Djibouti"] = 5000, ["Country_data_Dominica"] = 4200, ["Country_data_Dominican_Republic"] = 34000, ["Country_data_ECU"] = 13000, ["Country_data_EGY"] = 16000, ["Country_data_ENG"] = 52000, ["Country_data_ESA"] = 2500, ["Country_data_ESP"] = 85000, ["Country_data_EST"] = 17000, ["Country_data_ETH"] = 4600, ["Country_data_EU"] = 2600, ["Country_data_East_Germany"] = 28000, ["Country_data_Ecuador"] = 48000, ["Country_data_Egypt"] = 65000, ["Country_data_El_Salvador"] = 22000, ["Country_data_Empire_of_Japan"] = 4400, ["Country_data_England"] = 162000, ["Country_data_Equatorial_Guinea"] = 5500, ["Country_data_Eritrea"] = 5500, ["Country_data_Estonia"] = 68000, ["Country_data_Eswatini"] = 5100, ["Country_data_Ethiopia"] = 21000, ["Country_data_Europe"] = 2800, ["Country_data_European_Union"] = 8100, ["Country_data_FIJ"] = 4500, ["Country_data_FIN"] = 40000, ["Country_data_FRA"] = 115000, ["Country_data_FRG"] = 18000, ["Country_data_FRO"] = 2200, ["Country_data_FR_Yugoslavia"] = 4400, ["Country_data_Faroe_Islands"] = 5800, ["Country_data_Federated_States_of_Micronesia"] = 2700, ["Country_data_Fiji"] = 21000, ["Country_data_Finland"] = 113000, ["Country_data_Florida"] = 5800, ["Country_data_France"] = 333000, ["Country_data_French_Polynesia"] = 3900, ["Country_data_GAB"] = 2500, ["Country_data_GAM"] = 2300, ["Country_data_GBR"] = 67000, ["Country_data_GDR"] = 11000, ["Country_data_GEO"] = 16000, ["Country_data_GER"] = 95000, ["Country_data_GHA"] = 10000, ["Country_data_GIB"] = 2100, ["Country_data_GRE"] = 30000, ["Country_data_GUA"] = 5400, ["Country_data_GUI"] = 3400, ["Country_data_GUY"] = 2600, ["Country_data_Gabon"] = 8100, ["Country_data_Gambia"] = 7600, ["Country_data_Georgia"] = 8600, ["Country_data_Georgia_(U.S._state)"] = 3200, ["Country_data_Georgia_(country)"] = 53000, ["Country_data_German_Empire"] = 5700, ["Country_data_Germany"] = 291000, ["Country_data_Ghana"] = 46000, ["Country_data_Gibraltar"] = 5200, ["Country_data_Great_Britain"] = 127000, ["Country_data_Greece"] = 105000, ["Country_data_Greenland"] = 2800, ["Country_data_Grenada"] = 5700, ["Country_data_Guadeloupe"] = 2800, ["Country_data_Guam"] = 4600, ["Country_data_Guatemala"] = 22000, ["Country_data_Guinea"] = 9400, ["Country_data_Guinea-Bissau"] = 5500, ["Country_data_Guyana"] = 7700, ["Country_data_HAI"] = 3300, ["Country_data_HKG"] = 15000, ["Country_data_HON"] = 4400, ["Country_data_HUN"] = 45000, ["Country_data_Haiti"] = 9800, ["Country_data_Hawaii"] = 2200, ["Country_data_Honduras"] = 21000, ["Country_data_Hong_Kong"] = 51000, ["Country_data_Hungary"] = 114000, ["Country_data_IDN"] = 5800, ["Country_data_INA"] = 12000, ["Country_data_IND"] = 34000, ["Country_data_IRE"] = 12000, ["Country_data_IRI"] = 6700, ["Country_data_IRL"] = 24000, ["Country_data_IRN"] = 7300, ["Country_data_IRQ"] = 4800, ["Country_data_ISL"] = 9300, ["Country_data_ISR"] = 23000, ["Country_data_ITA"] = 102000, ["Country_data_Iceland"] = 46000, ["Country_data_Idaho"] = 2200, ["Country_data_Illinois"] = 4400, ["Country_data_India"] = 257000, ["Country_data_Indiana"] = 3400, ["Country_data_Indonesia"] = 71000, ["Country_data_Iowa"] = 2400, ["Country_data_Iran"] = 159000, ["Country_data_Iraq"] = 23000, ["Country_data_Ireland"] = 66000, ["Country_data_Isle_of_Man"] = 2900, ["Country_data_Israel"] = 92000, ["Country_data_Italy"] = 286000, ["Country_data_Ivory_Coast"] = 32000, ["Country_data_JAM"] = 12000, ["Country_data_JAP"] = 2300, ["Country_data_JOR"] = 4800, ["Country_data_JP"] = 8700, ["Country_data_JPN"] = 69000, ["Country_data_Jamaica"] = 44000, ["Country_data_Japan"] = 263000, ["Country_data_Jersey"] = 2500, ["Country_data_Jordan"] = 22000, ["Country_data_KAZ"] = 24000, ["Country_data_KEN"] = 10000, ["Country_data_KGZ"] = 4500, ["Country_data_KOR"] = 35000, ["Country_data_KOS"] = 3100, ["Country_data_KSA"] = 7500, ["Country_data_KUW"] = 4600, ["Country_data_Kazakhstan"] = 67000, ["Country_data_Kentucky"] = 2100, ["Country_data_Kenya"] = 35000, ["Country_data_Kingdom_of_France"] = 2200, ["Country_data_Kingdom_of_Great_Britain"] = 4600, ["Country_data_Kingdom_of_Italy"] = 4800, ["Country_data_Kiribati"] = 2600, ["Country_data_Kosovo"] = 10000, ["Country_data_Kuwait"] = 20000, ["Country_data_Kyrgyzstan"] = 11000, ["Country_data_LAO"] = 2200, ["Country_data_LAT"] = 17000, ["Country_data_LBN"] = 2900, ["Country_data_LBR"] = 2000, ["Country_data_LIB"] = 2600, ["Country_data_LIE"] = 3800, ["Country_data_LIT"] = 3700, ["Country_data_LTU"] = 15000, ["Country_data_LUX"] = 12000, ["Country_data_LVA"] = 3000, ["Country_data_Laos"] = 8000, ["Country_data_Latvia"] = 54000, ["Country_data_Lebanon"] = 23000, ["Country_data_Lesotho"] = 5400, ["Country_data_Liberia"] = 7900, ["Country_data_Libya"] = 9100, ["Country_data_Liechtenstein"] = 8900, ["Country_data_Lithuania"] = 54000, ["Country_data_Louisiana"] = 2300, ["Country_data_Luxembourg"] = 48000, ["Country_data_MAC"] = 3000, ["Country_data_MAD"] = 2400, ["Country_data_MAR"] = 15000, ["Country_data_MAS"] = 13000, ["Country_data_MDA"] = 8900, ["Country_data_MEX"] = 35000, ["Country_data_MGL"] = 3600, ["Country_data_MKD"] = 8500, ["Country_data_MLI"] = 4600, ["Country_data_MLT"] = 6500, ["Country_data_MNE"] = 8600, ["Country_data_MON"] = 4700, ["Country_data_MOZ"] = 2700, ["Country_data_MRI"] = 2200, ["Country_data_MYA"] = 3400, ["Country_data_MYS"] = 4200, ["Country_data_Macau"] = 6900, ["Country_data_Macedonia"] = 4600, ["Country_data_Madagascar"] = 9600, ["Country_data_Malawi"] = 6200, ["Country_data_Malaysia"] = 71000, ["Country_data_Maldives"] = 6700, ["Country_data_Mali"] = 21000, ["Country_data_Malta"] = 31000, ["Country_data_Manitoba"] = 3000, ["Country_data_Marshall_Islands"] = 3400, ["Country_data_Martinique"] = 2700, ["Country_data_Maryland"] = 3300, ["Country_data_Massachusetts"] = 2600, ["Country_data_Mauritania"] = 6000, ["Country_data_Mauritius"] = 8500, ["Country_data_Mexico"] = 114000, ["Country_data_Michigan"] = 3800, ["Country_data_Minas_Gerais"] = 2100, ["Country_data_Minnesota"] = 3700, ["Country_data_Missouri"] = 2500, ["Country_data_Moldova"] = 33000, ["Country_data_Monaco"] = 12000, ["Country_data_Mongolia"] = 11000, ["Country_data_Montana"] = 2400, ["Country_data_Montenegro"] = 33000, ["Country_data_Montserrat"] = 2400, ["Country_data_Morocco"] = 51000, ["Country_data_Mozambique"] = 8000, ["Country_data_Myanmar"] = 23000, ["Country_data_NAM"] = 4000, ["Country_data_NED"] = 72000, ["Country_data_NEP"] = 3400, ["Country_data_NGA"] = 8700, ["Country_data_NGR"] = 9400, ["Country_data_NIR"] = 11000, ["Country_data_NLD"] = 7400, ["Country_data_NOR"] = 35000, ["Country_data_NZ"] = 3700, ["Country_data_NZL"] = 39000, ["Country_data_Namibia"] = 11000, ["Country_data_Nauru"] = 2600, ["Country_data_Nazi_Germany"] = 10000, ["Country_data_Nepal"] = 31000, ["Country_data_Netherlands"] = 260000, ["Country_data_Netherlands_Antilles"] = 2400, ["Country_data_Nevada"] = 2100, ["Country_data_New_Brunswick"] = 2600, ["Country_data_New_Caledonia"] = 3300, ["Country_data_New_Hampshire"] = 2000, ["Country_data_New_Jersey"] = 4200, ["Country_data_New_South_Wales"] = 6400, ["Country_data_New_York"] = 4300, ["Country_data_New_York_(state)"] = 6800, ["Country_data_New_Zealand"] = 118000, ["Country_data_Newfoundland_and_Labrador"] = 2800, ["Country_data_Nicaragua"] = 9000, ["Country_data_Niger"] = 6300, ["Country_data_Nigeria"] = 66000, ["Country_data_North_Carolina"] = 3500, ["Country_data_North_Korea"] = 23000, ["Country_data_North_Macedonia"] = 33000, ["Country_data_Northern_Ireland"] = 27000, ["Country_data_Northern_Mariana_Islands"] = 2800, ["Country_data_Norway"] = 116000, ["Country_data_Nova_Scotia"] = 2800, ["Country_data_OMA"] = 3300, ["Country_data_Ohio"] = 5800, ["Country_data_Oklahoma"] = 2500, ["Country_data_Oman"] = 10000, ["Country_data_Ontario"] = 3900, ["Country_data_Ottoman_Empire"] = 3200, ["Country_data_PAK"] = 9000, ["Country_data_PAN"] = 6000, ["Country_data_PAR"] = 11000, ["Country_data_PER"] = 14000, ["Country_data_PHI"] = 13000, ["Country_data_PLE"] = 2000, ["Country_data_PNG"] = 3200, ["Country_data_POL"] = 61000, ["Country_data_POR"] = 37000, ["Country_data_PRK"] = 5000, ["Country_data_PRT"] = 3600, ["Country_data_PUR"] = 8100, ["Country_data_Pakistan"] = 51000, ["Country_data_Palau"] = 2600, ["Country_data_Palestine"] = 7700, ["Country_data_Panama"] = 30000, ["Country_data_Papua_New_Guinea"] = 8400, ["Country_data_Paraguay"] = 44000, ["Country_data_Pennsylvania"] = 3600, ["Country_data_Peru"] = 64000, ["Country_data_Philippines"] = 68000, ["Country_data_Poland"] = 240000, ["Country_data_Portugal"] = 116000, ["Country_data_Prussia"] = 2800, ["Country_data_Puerto_Rico"] = 34000, ["Country_data_QAT"] = 9500, ["Country_data_Qatar"] = 32000, ["Country_data_Quebec"] = 4400, ["Country_data_Queensland"] = 2300, ["Country_data_ROM"] = 15000, ["Country_data_ROU"] = 31000, ["Country_data_RSA"] = 36000, ["Country_data_RUS"] = 68000, ["Country_data_RWA"] = 2500, ["Country_data_Republic_of_China"] = 4600, ["Country_data_Republic_of_Ireland"] = 54000, ["Country_data_Republic_of_the_Congo"] = 8300, ["Country_data_Romania"] = 113000, ["Country_data_Russia"] = 253000, ["Country_data_Russian_Empire"] = 5600, ["Country_data_Rwanda"] = 8500, ["Country_data_SAM"] = 3700, ["Country_data_SCG"] = 3000, ["Country_data_SCO"] = 29000, ["Country_data_SEN"] = 8500, ["Country_data_SER"] = 3700, ["Country_data_SGP"] = 4500, ["Country_data_SIN"] = 7400, ["Country_data_SLE"] = 2000, ["Country_data_SLO"] = 22000, ["Country_data_SLV"] = 3200, ["Country_data_SMR"] = 3600, ["Country_data_SPA"] = 5500, ["Country_data_SRB"] = 30000, ["Country_data_SRI"] = 5300, ["Country_data_SUI"] = 50000, ["Country_data_SUR"] = 2300, ["Country_data_SVK"] = 32000, ["Country_data_SVN"] = 7300, ["Country_data_SWE"] = 65000, ["Country_data_SWI"] = 5100, ["Country_data_SYR"] = 3900, ["Country_data_Saint_Kitts_and_Nevis"] = 4900, ["Country_data_Saint_Lucia"] = 5300, ["Country_data_Saint_Vincent_and_the_Grenadines"] = 5000, ["Country_data_Samoa"] = 8000, ["Country_data_San_Marino"] = 9600, ["Country_data_Saskatchewan"] = 4000, ["Country_data_Saudi_Arabia"] = 37000, ["Country_data_Scotland"] = 101000, ["Country_data_Senegal"] = 31000, ["Country_data_Serbia"] = 103000, ["Country_data_Serbia_and_Montenegro"] = 5400, ["Country_data_Seychelles"] = 5800, ["Country_data_Sierra_Leone"] = 7600, ["Country_data_Singapore"] = 50000, ["Country_data_Slovakia"] = 95000, ["Country_data_Slovenia"] = 90000, ["Country_data_Solomon_Islands"] = 4700, ["Country_data_Somalia"] = 6400, ["Country_data_South_Africa"] = 120000, ["Country_data_South_Australia"] = 2300, ["Country_data_South_Carolina"] = 3100, ["Country_data_South_Korea"] = 115000, ["Country_data_South_Sudan"] = 4800, ["Country_data_Soviet_Union"] = 76000, ["Country_data_Spain"] = 275000, ["Country_data_Sri_Lanka"] = 35000, ["Country_data_Sudan"] = 8700, ["Country_data_Suriname"] = 6800, ["Country_data_Sweden"] = 165000, ["Country_data_Switzerland"] = 149000, ["Country_data_Syria"] = 21000, ["Country_data_São_Tomé_and_Príncipe"] = 3400, ["Country_data_TAN"] = 3500, ["Country_data_TCH"] = 12000, ["Country_data_THA"] = 25000, ["Country_data_TJK"] = 2900, ["Country_data_TKM"] = 3100, ["Country_data_TOG"] = 2100, ["Country_data_TON"] = 2000, ["Country_data_TPE"] = 19000, ["Country_data_TRI"] = 5100, ["Country_data_TTO"] = 2200, ["Country_data_TUN"] = 12000, ["Country_data_TUR"] = 33000, ["Country_data_TWN"] = 2200, ["Country_data_Taiwan"] = 27000, ["Country_data_Tajikistan"] = 9700, ["Country_data_Tanzania"] = 22000, ["Country_data_Tennessee"] = 2200, ["Country_data_Texas"] = 5800, ["Country_data_Thailand"] = 95000, ["Country_data_Timor-Leste"] = 5100, ["Country_data_Togo"] = 7600, ["Country_data_Tonga"] = 6700, ["Country_data_Trinidad_and_Tobago"] = 24000, ["Country_data_Tunisia"] = 45000, ["Country_data_Turkey"] = 139000, ["Country_data_Turkmenistan"] = 8400, ["Country_data_Turks_and_Caicos_Islands"] = 2800, ["Country_data_Tuvalu"] = 2600, ["Country_data_U.S."] = 2000, ["Country_data_U.S._Virgin_Islands"] = 4900, ["Country_data_UAE"] = 11000, ["Country_data_UGA"] = 5300, ["Country_data_UK"] = 19000, ["Country_data_UKGBI"] = 3500, ["Country_data_UKR"] = 44000, ["Country_data_URS"] = 19000, ["Country_data_URU"] = 16000, ["Country_data_US"] = 5700, ["Country_data_USA"] = 151000, ["Country_data_USSR"] = 5300, ["Country_data_UZB"] = 14000, ["Country_data_Uganda"] = 22000, ["Country_data_Ukraine"] = 118000, ["Country_data_United_Arab_Emirates"] = 38000, ["Country_data_United_Kingdom"] = 157000, ["Country_data_United_Kingdom_of_Great_Britain_and_Ireland"] = 4900, ["Country_data_United_Nations"] = 4400, ["Country_data_United_States"] = 434000, ["Country_data_United_States_of_America"] = 4500, ["Country_data_Uruguay"] = 52000, ["Country_data_Uzbekistan"] = 36000, ["Country_data_VEN"] = 18000, ["Country_data_VIE"] = 7300, ["Country_data_Vanuatu"] = 5100, ["Country_data_Vatican_City"] = 2900, ["Country_data_Venezuela"] = 65000, ["Country_data_Victoria"] = 2400, ["Country_data_Vietnam"] = 46000, ["Country_data_Virginia"] = 3100, ["Country_data_WAL"] = 19000, ["Country_data_Wales"] = 67000, ["Country_data_Washington"] = 2700, ["Country_data_Washington,_D.C."] = 2200, ["Country_data_Washington_(state)"] = 3400, ["Country_data_West_Germany"] = 52000, ["Country_data_West_Indies"] = 2800, ["Country_data_Wisconsin"] = 5600, ["Country_data_Wyoming"] = 2100, ["Country_data_YUG"] = 11000, ["Country_data_Yemen"] = 8000, ["Country_data_Yugoslavia"] = 34000, ["Country_data_ZAF"] = 5400, ["Country_data_ZAM"] = 3800, ["Country_data_ZIM"] = 9200, ["Country_data_Zambia"] = 11000, ["Country_data_Zimbabwe"] = 32000, ["Country_data_none"] = 2100, ["Country_flagbio"] = 40000, ["Country_in_the_Eurovision_Song_Contest"] = 2900, ["Country_name"] = 59000, ["Country_prefix_the"] = 176000, ["Country_showdata"] = 7000, ["Country_topics"] = 25000, ["County"] = 7000, ["County_(judet)_of_Romania"] = 3300, ["Course_assignment"] = 4200, ["Course_details"] = 8500, ["Course_instructor"] = 3200, ["Cquote"] = 37000, ["Cr"] = 5000, ["Cr-rt"] = 2400, ["Create_taxonomy/link"] = 150000, ["Creative_Commons_text_attribution_notice"] = 6300, ["Cref2"] = 2900, ["Cricinfo"] = 24000, ["Cricket_squad2_player"] = 2700, ["Cricketarchive"] = 3000, ["Croatian_Census_2021"] = 4200, ["Cross"] = 4100, ["Crossreference"] = 2600, ["Crossreference/styles.css"] = 2600, ["Cs1_config"] = 13000, ["Cslist"] = 3700, ["Csv"] = 76000, ["Currency"] = 5400, ["Current_events"] = 9400, ["Current_events/styles.css"] = 9400, ["Current_leader"] = 5100, ["Currentdate"] = 24000, ["Cvt"] = 171000, ["Cycling_Archives"] = 4300, ["Cycling_data_DAT"] = 2000, ["Cycling_data_LTD"] = 2400, ["Cycling_data_MOV_men"] = 2300, ["Cycling_data_SOQ"] = 2200, ["Cycling_data_TVL"] = 2400, ["Cycling_data_UEX"] = 2300, ["Cycling_team_link"] = 14000, ["Module:CFB_schedule"] = 29000, ["Module:CS1_identifiers"] = 507000, ["Module:CallAssert"] = 122000, ["Module:Canada_NTS"] = 2100, ["Module:Canada_NTS/data"] = 2100, ["Module:Canada_by_province_category_navigation"] = 9900, ["Module:Catalog_lookup_link"] = 61000, ["Module:Category_described_in_year"] = 6400, ["Module:Category_described_in_year/config"] = 6400, ["Module:Category_disambiguation"] = 2700, ["Module:Category_handler"] = 4990000, ["Module:Category_handler/blacklist"] = 4990000, ["Module:Category_handler/config"] = 4990000, ["Module:Category_handler/data"] = 4990000, ["Module:Category_handler/shared"] = 4990000, ["Module:Category_main_article"] = 221000, ["Module:Category_more_if_exists"] = 49000, ["Module:Category_pair"] = 7100, ["Module:Category_see_also"] = 55000, ["Module:Category_series_navigation"] = 598000, ["Module:Category_series_navigation/styles.css"] = 598000, ["Module:Celestial_object_quadrangle"] = 2100, ["Module:Chart"] = 2900, ["Module:Chart/Default_colors"] = 2900, ["Module:Check_DYK_hook"] = 126000, ["Module:Check_blp_parameter"] = 2910000, ["Module:Check_for_conflicting_parameters"] = 4730000, ["Module:Check_for_deprecated_parameters"] = 989000, ["Module:Check_for_unknown_parameters"] = 23400000, ["Module:Check_height"] = 153000, ["Module:Check_isxn"] = 16000, ["Module:Check_winner_by_scores"] = 16000, ["Module:Checkuser"] = 90000, ["Module:Chem2"] = 11000, ["Module:Chem2/styles.css"] = 11000, ["Module:Citation/CS1"] = 6540000, ["Module:Citation/CS1/COinS"] = 6540000, ["Module:Citation/CS1/Configuration"] = 6670000, ["Module:Citation/CS1/Date_validation"] = 6540000, ["Module:Citation/CS1/Identifiers"] = 6660000, ["Module:Citation/CS1/Suggestions"] = 26000, ["Module:Citation/CS1/Utilities"] = 6670000, ["Module:Citation/CS1/Whitelist"] = 6540000, ["Module:Citation/CS1/styles.css"] = 6670000, ["Module:Citation_mode"] = 49000, ["Module:Cite"] = 53000, ["Module:Cite/config"] = 70000, ["Module:Cite_IUCN"] = 71000, ["Module:Cite_Q"] = 17000, ["Module:Cite_Q/config"] = 17000, ["Module:Cite_taxon"] = 55000, ["Module:Cite_tweet"] = 48000, ["Module:Clade"] = 10000, ["Module:Class"] = 62000, ["Module:Class/definition.json"] = 62000, ["Module:Class/styles.css"] = 39000, ["Module:Class_mask"] = 461000, ["Module:Clickable_button"] = 1870000, ["Module:Collapsible_list"] = 70000, ["Module:College_color"] = 137000, ["Module:College_color/data"] = 137000, ["Module:Color_contrast"] = 575000, ["Module:Color_contrast/colors"] = 578000, ["Module:Comma-separated_list"] = 3700, ["Module:Commons_link"] = 856000, ["Module:Compact_list"] = 2000, ["Module:Complex_date"] = 47000, ["Module:Conservation_status"] = 89000, ["Module:Contentious_topics/talk_notice"] = 21000, ["Module:Convert"] = 1510000, ["Module:Convert/data"] = 1510000, ["Module:Convert/helper"] = 13000, ["Module:Convert/text"] = 1510000, ["Module:Convert/wikidata"] = 2700, ["Module:Convert/wikidata/data"] = 2700, ["Module:ConvertIB"] = 799000, ["Module:ConvertIB/data"] = 274000, ["Module:ConvertNumeric"] = 21000, ["Module:Convert_character_width"] = 2700, ["Module:Convert_character_width/data"] = 2700, ["Module:Coordinates"] = 1410000, ["Module:Coordinates/styles.css"] = 1410000, ["Module:Copied"] = 43000, ["Module:Country2nationality"] = 97000, ["Module:CountryAdjectiveDemonym"] = 116000, ["Module:CountryAdjectiveDemonym/Adjectives"] = 116000, ["Module:CountryAdjectiveDemonym/Demonyms"] = 116000, ["Module:CountryAdjectiveDemonym/The"] = 116000, ["Module:CountryData"] = 475000, ["Module:CountryData/cacheA"] = 165000, ["Module:CountryData/cacheB"] = 81000, ["Module:CountryData/cacheC"] = 59000, ["Module:CountryData/cacheD"] = 56000, ["Module:CountryData/cacheE"] = 38000, ["Module:CountryData/cacheF"] = 26000, ["Module:CountryData/cacheG"] = 27000, ["Module:CountryData/cacheH"] = 18000, ["Module:CountryData/cacheI"] = 17000, ["Module:CountryData/cacheJ"] = 11000, ["Module:CountryData/cacheK"] = 11000, ["Module:CountryData/summary"] = 475000, ["Module:Country_adjective"] = 4700, ["Module:Country_alias"] = 62000, ["Module:Country_alias/data"] = 62000, ["Module:Cslist"] = 3700, ["Module:Currency"] = 5500, ["Module:Currency/Presentation"] = 5500, ["Module:Current_leader"] = 5300, ["Module:Current_leader/data"] = 5300, } n1wqoeq6ajuyzaw157ew09067nsjvd9 366135 366134 2026-08-21T03:52:54Z ChiK 1136 ೧ revisions imported from [[:en:Module:Transclusion_count/data/C]] 366134 Scribunto text/plain return { ["C"] = 800000, ["C-Class"] = 17000, ["C-SPAN"] = 13000, ["C-cmn"] = 2800, ["C-pl"] = 51000, ["C."] = 352000, ["CAN"] = 21000, ["CANelec"] = 18000, ["CANelec/gain"] = 3500, ["CANelec/hold"] = 6600, ["CANelec/source"] = 11000, ["CANelec/top"] = 8900, ["CANelec/total"] = 9400, ["CAS"] = 3800, ["CAS_Registry_Number"] = 3800, ["CBB_Yearly_Record_End"] = 3500, ["CBB_Yearly_Record_Entry"] = 3600, ["CBB_Yearly_Record_Start"] = 3400, ["CBB_Yearly_Record_Subhead"] = 4200, ["CBB_Yearly_Record_Subtotal"] = 3300, ["CBB_roster/Footer"] = 11000, ["CBB_roster/Header"] = 11000, ["CBB_roster/Player"] = 11000, ["CBB_schedule_end"] = 14000, ["CBB_schedule_entry"] = 14000, ["CBB_schedule_start"] = 14000, ["CBB_standings_end"] = 19000, ["CBB_standings_entry"] = 19000, ["CBB_standings_start"] = 19000, ["CBB_yearly_record_end"] = 4700, ["CBB_yearly_record_end/legend"] = 4100, ["CBB_yearly_record_entry"] = 4600, ["CBB_yearly_record_start"] = 4500, ["CBB_yearly_record_subhead"] = 4300, ["CBB_yearly_record_subtotal"] = 4400, ["CBSB_Standings_End"] = 2700, ["CBSB_Standings_Entry"] = 2700, ["CBSB_Standings_Start"] = 2600, ["CBSB_link"] = 4100, ["CBSB_standings_end"] = 5500, ["CBSB_standings_entry"] = 5500, ["CBSB_standings_start"] = 5500, ["CC0"] = 4000, ["CC_BY-SA_2.5"] = 2100, ["CC_BY-SA_2.5,_2.0,_1.0"] = 2100, ["CC_BY-SA_3.0"] = 21000, ["CC_BY-SA_3.0_migrated"] = 19000, ["CC_BY-SA_4.0"] = 14000, ["CC_BY_2.5"] = 2600, ["CC_BY_3.0"] = 7000, ["CFB_Standings_End"] = 16000, ["CFB_Standings_Entry"] = 16000, ["CFB_Standings_Start"] = 16000, ["CFB_Yearly_Record_End"] = 7900, ["CFB_Yearly_Record_End/legend"] = 2900, ["CFB_Yearly_Record_Entry"] = 7900, ["CFB_Yearly_Record_Start"] = 7900, ["CFB_Yearly_Record_Subhead"] = 8000, ["CFB_Yearly_Record_Subtotal"] = 7800, ["CFB_schedule"] = 29000, ["CFB_schedule_entry"] = 22000, ["CFB_standings_end"] = 39000, ["CFB_standings_entry"] = 39000, ["CFB_standings_start"] = 39000, ["CFL_Year"] = 7200, ["CGF_year"] = 3800, ["CGNDB_URL"] = 5400, ["CHE"] = 12000, ["CHI"] = 3300, ["CHL"] = 4300, ["CHN"] = 15000, ["CN"] = 4000, ["CO2"] = 3500, ["COI"] = 14000, ["COIUL"] = 131000, ["COI_editnotice"] = 7800, ["COL"] = 5600, ["COLON"] = 14000, ["CRI"] = 2300, ["CRO"] = 6600, ["CS1_config"] = 15000, ["CSK"] = 2800, ["CSS_crop"] = 2100, ["CSS_image_crop"] = 9500, ["CUB"] = 4200, ["CURRENTDATE"] = 3600, ["CYP"] = 2200, ["CZE"] = 16000, ["Calendar"] = 2500, ["Calendar/styles.css"] = 2800, ["California/color"] = 12000, ["Call_sign_disambiguation"] = 3100, ["Campaignbox"] = 26000, ["CanProvName"] = 19000, ["CanadaByProvinceCatNav"] = 9500, ["CanadaProvinceThe"] = 4500, ["Canada_NTS_Map_Sheet"] = 2100, ["Canada_by_province_category_navigation"] = 9900, ["Canadian_English"] = 13000, ["Canadian_Parliament_links"] = 5100, ["Canadian_election_result"] = 18000, ["Canadian_election_result/gain"] = 3600, ["Canadian_election_result/hold"] = 6900, ["Canadian_election_result/source"] = 12000, ["Canadian_election_result/top"] = 18000, ["Canadian_election_result/total"] = 14000, ["Canadian_party_colour"] = 11000, ["Canadian_party_colour/colour"] = 23000, ["Canadian_party_colour/colour/default"] = 22000, ["Canadian_party_colour/name"] = 19000, ["Canadian_party_colour/name/default"] = 8300, ["Canadian_province_name"] = 19000, ["Canned_search"] = 8500, ["Cascite"] = 17000, ["Caselaw_source"] = 4800, ["Cassini-Ehess"] = 3100, ["Cast_listing"] = 29000, ["Castlist"] = 5600, ["Cat"] = 471000, ["CatAutoTOC"] = 373000, ["CatRel"] = 4100, ["CatTrack"] = 20000, ["Cat_class"] = 6300, ["Cat_main"] = 149000, ["Cat_more"] = 102000, ["Cat_more_if_exists"] = 29000, ["Cat_see_also"] = 3800, ["Catalog_lookup_link"] = 60000, ["Catalog_of_Fishes"] = 9700, ["Catalogue_of_Life"] = 24000, ["Category"] = 2400, ["Category-Class"] = 14000, ["Category-inline"] = 11000, ["Category_TOC"] = 80000, ["Category_TOC/tracking"] = 36000, ["Category_U.S._State_elections_by_year"] = 7500, ["Category_U.S._State_elections_by_year/core"] = 7500, ["Category_class"] = 25000, ["Category_described_in_year"] = 6400, ["Category_diffuse"] = 11000, ["Category_disambiguation"] = 2700, ["Category_explanation"] = 415000, ["Category_handler"] = 2720000, ["Category_if_exists"] = 94000, ["Category_ifexist"] = 4900, ["Category_importance"] = 8800, ["Category_link"] = 691000, ["Category_link/core"] = 691000, ["Category_link_if_exists"] = 81000, ["Category_link_with_count"] = 9200, ["Category_link_without_namespace"] = 801000, ["Category_links"] = 9900, ["Category_main_article"] = 221000, ["Category_more"] = 143000, ["Category_more_if_exists"] = 49000, ["Category_ordered_by_date"] = 13000, ["Category_other"] = 1110000, ["Category_pair"] = 2200, ["Category_redirect"] = 159000, ["Category_redirect/core"] = 159000, ["Category_see_also"] = 55000, ["Category_see_also/Category_pair_check"] = 55000, ["Category_see_also_if_exists"] = 95000, ["Category_see_also_if_exists_2"] = 101000, ["Category_series_navigation"] = 598000, ["Category_title"] = 2400, ["Catexp"] = 5900, ["CathEncy"] = 2300, ["Catholic"] = 3700, ["Catholic_Encyclopedia"] = 5100, ["Catmain"] = 29000, ["Catmore"] = 9800, ["Catseealso"] = 2300, ["Cbb_link"] = 10000, ["Cbignore"] = 132000, ["Cbsb_link"] = 2500, ["Cc-by-2.5"] = 2600, ["Cc-by-3.0"] = 7000, ["Cc-by-sa-2.5"] = 2100, ["Cc-by-sa-2.5,2.0,1.0"] = 2100, ["Cc-by-sa-3.0"] = 20000, ["Cc-by-sa-3.0-migrated"] = 19000, ["Cc-by-sa-4.0"] = 13000, ["Cc-zero"] = 3800, ["CensusAU"] = 9100, ["Census_2016_AUS"] = 5700, ["Census_2021_AUS"] = 6200, ["Cent"] = 6900, ["Center"] = 301000, ["Centralized_discussion"] = 7200, ["Centralized_discussion/core"] = 7200, ["Centralized_discussion/styles.css"] = 7200, ["Centre"] = 4200, ["Century"] = 16000, ["Century_name_from_decade"] = 2400, ["Century_name_from_decade_or_year"] = 127000, ["Century_name_from_title_decade"] = 7600, ["Century_name_from_title_year"] = 9200, ["Certification_Cite_Ref"] = 38000, ["Certification_Table_Bottom"] = 37000, ["Certification_Table_Entry"] = 39000, ["Certification_Table_Entry/Foot"] = 37000, ["Certification_Table_Entry/Foot/helper"] = 37000, ["Certification_Table_Entry/MexicanAward"] = 2300, ["Certification_Table_Entry/Region"] = 38000, ["Certification_Table_Entry/Sales"] = 37000, ["Certification_Table_Entry/Sales/AustrianPeriod"] = 2500, ["Certification_Table_Entry/Sales/BelgianPeriod"] = 2400, ["Certification_Table_Entry/Sales/DanishPeriod"] = 4400, ["Certification_Table_Entry/Sales/DanishPeriodHelper1"] = 4400, ["Certification_Table_Entry/Sales/DanishPeriodHelper2"] = 4400, ["Certification_Table_Entry/Sales/FrenchPeriod"] = 2200, ["Certification_Table_Entry/Sales/FrenchPeriodAlbum"] = 2000, ["Certification_Table_Entry/Sales/GermanPeriod"] = 4100, ["Certification_Table_Entry/Sales/ItalianHelper"] = 4500, ["Certification_Table_Entry/Sales/MexicanSales"] = 2200, ["Certification_Table_Entry/Sales/NewZealandPeriod"] = 7800, ["Certification_Table_Entry/Sales/SpanishSinglesPeriod"] = 2800, ["Certification_Table_Entry/Sales/SwedishPeriod"] = 2300, ["Certification_Table_Separator"] = 3300, ["Certification_Table_Top"] = 39000, ["Cfb_link"] = 26000, ["Cfd_all"] = 6100, ["Cfm_full"] = 2400, ["Cfr_full"] = 3500, ["Chart_bottom"] = 4500, ["Chart_top"] = 4500, ["Check_completeness_of_transclusions"] = 64000, ["Check_talk"] = 63000, ["Check_talk_wp"] = 1460000, ["Check_winner_by_scores"] = 16000, ["CheckedSockpuppet"] = 7200, ["Checked_sockpuppet"] = 18000, ["Checkedsockpuppet"] = 5300, ["Checkip"] = 12000, ["Checkuser"] = 90000, ["Checkuser/Hacky_user_info_card_wrapper_template_(do_not_use_outside_of_modules)"] = 53000, ["Checkuserblock-account"] = 44000, ["Chem"] = 6100, ["Chem/atom"] = 6000, ["Chem/link"] = 6100, ["Chem2"] = 11000, ["Chem_molar_mass"] = 22000, ["Chem_molar_mass/format"] = 22000, ["Chembox"] = 16000, ["Chembox/styles.css"] = 16000, ["Chembox_3DMet"] = 15000, ["Chembox_AllOtherNames"] = 14000, ["Chembox_AllOtherNames/format"] = 14000, ["Chembox_Appearance"] = 7200, ["Chembox_BoilingPt"] = 4200, ["Chembox_CASNo"] = 15000, ["Chembox_CASNo/format"] = 15000, ["Chembox_CalcTemperatures"] = 7600, ["Chembox_ChEBI"] = 15000, ["Chembox_ChEBI/format"] = 15000, ["Chembox_ChEMBL"] = 15000, ["Chembox_ChEMBL/format"] = 15000, ["Chembox_ChemSpiderID"] = 15000, ["Chembox_ChemSpiderID/format"] = 15000, ["Chembox_CompTox"] = 15000, ["Chembox_CompTox/format"] = 15000, ["Chembox_Datapage_check"] = 16000, ["Chembox_Density"] = 5700, ["Chembox_DrugBank"] = 15000, ["Chembox_DrugBank/format"] = 15000, ["Chembox_ECHA"] = 8300, ["Chembox_ECNumber"] = 15000, ["Chembox_ECNumber/format"] = 15000, ["Chembox_Elements"] = 15000, ["Chembox_Elements/molecular_formula"] = 21000, ["Chembox_FlashPt"] = 2100, ["Chembox_Footer"] = 15000, ["Chembox_Footer/tracking"] = 16000, ["Chembox_GHS_(set)"] = 4500, ["Chembox_Hazards"] = 13000, ["Chembox_IUPHAR_ligand"] = 15000, ["Chembox_IUPHAR_ligand/format"] = 15000, ["Chembox_Identifiers"] = 15000, ["Chembox_InChI"] = 15000, ["Chembox_InChI/format"] = 15000, ["Chembox_Indexlist"] = 15000, ["Chembox_Jmol"] = 15000, ["Chembox_Jmol/format"] = 15000, ["Chembox_KEGG"] = 15000, ["Chembox_KEGG/format"] = 15000, ["Chembox_MeltingPt"] = 6600, ["Chembox_OtherCpds"] = 2100, ["Chembox_Properties"] = 15000, ["Chembox_PubChem"] = 15000, ["Chembox_PubChem/format"] = 15000, ["Chembox_RTECS"] = 15000, ["Chembox_RTECS/format"] = 15000, ["Chembox_Related"] = 4200, ["Chembox_SMILES"] = 15000, ["Chembox_SMILES/format"] = 15000, ["Chembox_SolubilityInWater"] = 4500, ["Chembox_Structure"] = 2400, ["Chembox_UNII"] = 15000, ["Chembox_UNII/format"] = 15000, ["Chembox_headerbar"] = 16000, ["Chembox_image"] = 14000, ["Chembox_image_cell"] = 14000, ["Chembox_image_sbs"] = 14000, ["Chembox_parametercheck"] = 15000, ["Chembox_setDatarow"] = 5500, ["Chembox_setHeader"] = 5500, ["Chembox_templatePar/formatPreviewMessage"] = 16000, ["Chembox_verification"] = 6900, ["Chemspidercite"] = 11000, ["Chessgames_player"] = 3800, ["Chinese"] = 3500, ["Circa"] = 469000, ["Circular_reference"] = 3800, ["Citation"] = 412000, ["Citation/make_link"] = 7900, ["Citation/styles.css"] = 53000, ["Citation_needed"] = 636000, ["Citation_needed_span"] = 3600, ["Citation_style"] = 4500, ["Cite_AV_media"] = 80000, ["Cite_AV_media_notes"] = 34000, ["Cite_Appletons'"] = 2400, ["Cite_Australian_Dictionary_of_Biography"] = 4900, ["Cite_Box_Office_Mojo"] = 2500, ["Cite_Catholic_Encyclopedia"] = 8600, ["Cite_DCB"] = 3300, ["Cite_DGRBM"] = 2200, ["Cite_DNB"] = 20000, ["Cite_EB1911"] = 26000, ["Cite_FTP"] = 2500, ["Cite_GBIF"] = 4300, ["Cite_GNIS"] = 2400, ["Cite_Gaia_DR3"] = 3100, ["Cite_Google_Maps"] = 20000, ["Cite_Hansard"] = 2700, ["Cite_IUCN"] = 71000, ["Cite_Instagram"] = 7400, ["Cite_Jewish_Encyclopedia"] = 3200, ["Cite_LCDB"] = 2000, ["Cite_Metacritic"] = 2300, ["Cite_NIE"] = 3500, ["Cite_NSW_Parliament"] = 3300, ["Cite_NSW_SHR"] = 2700, ["Cite_NatureServe"] = 2900, ["Cite_ODNB"] = 22000, ["Cite_OED"] = 2500, ["Cite_POWO"] = 6300, ["Cite_Q"] = 17000, ["Cite_QHR"] = 3100, ["Cite_QPN"] = 4100, ["Cite_Rotten_Tomatoes"] = 2900, ["Cite_Rowlett"] = 2500, ["Cite_Russian_law"] = 7800, ["Cite_Ryan"] = 3300, ["Cite_Sports-Reference"] = 55000, ["Cite_USGov"] = 13000, ["Cite_WoRMS"] = 13000, ["Cite_act"] = 4000, ["Cite_arXiv"] = 7100, ["Cite_bcgnis"] = 3700, ["Cite_book"] = 1910000, ["Cite_certification"] = 43000, ["Cite_certification/Title"] = 39000, ["Cite_certification/URL"] = 43000, ["Cite_certification/archivedate"] = 7600, ["Cite_certification/archiveurl"] = 7600, ["Cite_cgndb"] = 5400, ["Cite_comic"] = 2100, ["Cite_conference"] = 22000, ["Cite_court"] = 6800, ["Cite_dictionary"] = 3600, ["Cite_document"] = 2000, ["Cite_encyclopedia"] = 243000, ["Cite_episode"] = 19000, ["Cite_gnis"] = 35000, ["Cite_instagram"] = 3100, ["Cite_interview"] = 11000, ["Cite_iucn"] = 70000, ["Cite_journal"] = 1180000, ["Cite_legislation_UK"] = 4000, ["Cite_magazine"] = 381000, ["Cite_map"] = 60000, ["Cite_news"] = 1930000, ["Cite_newspaper_The_Times"] = 7200, ["Cite_patent"] = 7300, ["Cite_patent/authors"] = 6100, ["Cite_patent/core"] = 7600, ["Cite_peakbagger"] = 5300, ["Cite_podcast"] = 6200, ["Cite_press_release"] = 86000, ["Cite_report"] = 76000, ["Cite_rowlett"] = 2500, ["Cite_ship_register"] = 2000, ["Cite_simbad"] = 5600, ["Cite_sports-reference"] = 60000, ["Cite_taxon"] = 5500, ["Cite_tech_report"] = 3100, ["Cite_thesis"] = 50000, ["Cite_tweet"] = 48000, ["Cite_video"] = 13000, ["Cite_video_game"] = 3400, ["Cite_web"] = 5420000, ["Cite_wikisource"] = 7100, ["Cite_wikisource/make_link"] = 65000, ["Citeweb"] = 4100, ["Civil_navigation"] = 2100, ["Cjersey"] = 3000, ["Cl"] = 218000, ["Clade"] = 10000, ["Clade/styles.css"] = 10000, ["Clarification_needed"] = 2600, ["Clarify"] = 45000, ["Class"] = 39000, ["Class/colour"] = 19000, ["Class/icon"] = 21000, ["Class_mask"] = 461000, ["Classicon"] = 5200, ["Clc"] = 7400, ["Cleanup"] = 8400, ["Cleanup_reorganize"] = 2300, ["Cleanup_rewrite"] = 6800, ["Clear"] = 3750000, ["Clear_left"] = 26000, ["Clear_right"] = 2800, ["Clerk_Request"] = 2500, ["Clerk_note"] = 11000, ["Clerknote"] = 8600, ["Clickable_button"] = 1870000, ["Clickable_button_2"] = 227000, ["Closed_access"] = 5600, ["Closed_peer_review_page"] = 3200, ["Closed_rfc_bottom"] = 2200, ["Closed_rfc_top"] = 2900, ["Clr"] = 3100, ["Club_players_category"] = 9500, ["Clubplayerscat"] = 9500, ["Cmbox"] = 538000, ["Cn"] = 113000, ["Cnote2"] = 2900, ["Cnote2_Begin"] = 2900, ["Cnote2_End"] = 2900, ["Coat_of_arms"] = 2500, ["Cob"] = 15000, ["Code"] = 297000, ["Cof_family"] = 4500, ["Cof_genus"] = 5300, ["Col-1-of-2"] = 2400, ["Col-2"] = 186000, ["Col-2-of-2"] = 2300, ["Col-3"] = 11000, ["Col-4"] = 3800, ["Col-begin"] = 231000, ["Col-begin/styles.css"] = 231000, ["Col-break"] = 229000, ["Col-end"] = 231000, ["Col-float"] = 4900, ["Col-float-break"] = 4700, ["Col-float-end"] = 4800, ["Col-float/styles.css"] = 4900, ["Col-start"] = 9900, ["Col_div"] = 2200, ["Colbegin"] = 24000, ["Colend"] = 27000, ["Collapse"] = 8800, ["Collapse_bottom"] = 56000, ["Collapse_top"] = 57000, ["Collapse_top/styles.css"] = 57000, ["Collapsebottom"] = 4100, ["Collapsed_infobox_section_begin"] = 4400, ["Collapsed_infobox_section_begin/cont"] = 4400, ["Collapsed_infobox_section_begin/styles.css"] = 4400, ["Collapsed_infobox_section_end"] = 4400, ["Collapsetop"] = 4100, ["Collapsible_list"] = 68000, ["Collapsible_option"] = 151000, ["Colledge:_Ships_RN"] = 3000, ["College"] = 12000, ["CollegePrimaryHeader"] = 8200, ["CollegePrimaryStyle"] = 111000, ["CollegeSecondaryStyle"] = 11000, ["College_Athlete_Recruit_End"] = 2800, ["College_Athlete_Recruit_Entry"] = 2900, ["College_Athlete_Recruit_Start"] = 2900, ["College_athlete_recruit_end"] = 4900, ["College_athlete_recruit_entry"] = 5200, ["College_athlete_recruit_start"] = 5200, ["College_color_list"] = 4400, ["College_sports_team_link"] = 31000, ["Colon"] = 20000, ["Color"] = 501000, ["Color/styles.css"] = 501000, ["Color_box"] = 86000, ["Colorbox"] = 2600, ["Colorbull"] = 4600, ["Colored_link"] = 106000, ["Coloredlink"] = 2700, ["Colors"] = 3600, ["Colour"] = 5500, ["Colour_box"] = 2500, ["Coloured_link"] = 12000, ["Column"] = 4300, ["Column/styles.css"] = 4300, ["Columns-end"] = 4000, ["Columns-list"] = 125000, ["Columns-start"] = 4000, ["Comic_Book_DB"] = 3500, ["Comicbookdb"] = 3400, ["Comics-replaceability"] = 2700, ["Comics_infobox_sec/formcat"] = 3400, ["Comics_infobox_sec/genre"] = 4200, ["Comics_infobox_sec/genrecat"] = 3800, ["Comics_infobox_sec/styles.css"] = 8100, ["Comma-separated_list"] = 3700, ["Comma-separated_list/styles.css"] = 3700, ["Comma_separated_entries"] = 522000, ["Comment"] = 7600, ["Committed_identity"] = 3200, ["Committed_identity/styles.css"] = 3200, ["Commons"] = 73000, ["Commons-inline"] = 25000, ["Commons_cat"] = 45000, ["Commons_category"] = 904000, ["Commons_category-inline"] = 177000, ["Commons_category_inline"] = 5700, ["Commonscat"] = 59000, ["Commonscat-inline"] = 14000, ["Commonscat_inline"] = 2200, ["Commonscatinline"] = 7200, ["Compact_TOC"] = 7300, ["Compact_ToC"] = 3200, ["Compose_molecular_formula"] = 2400, ["Composition_bar"] = 13000, ["Confirmed"] = 20000, ["Confused"] = 3200, ["Confusing"] = 2400, ["CongBio"] = 9400, ["CongLinks"] = 4800, ["Connected_contributor"] = 21000, ["Connected_contributor_(paid)"] = 10000, ["Constellation_navbox"] = 9900, ["Container"] = 14000, ["Container_cat"] = 9100, ["Container_category"] = 92000, ["Containercat"] = 2800, ["Contains_special_characters"] = 5300, ["Contains_special_characters/core"] = 5300, ["Contains_special_characters/styles.css"] = 5300, ["Content_category"] = 7900, ["Contentious_topics/Additional_restrictions.json"] = 21000, ["Contentious_topics/Arab-Israeli_editnotice"] = 6900, ["Contentious_topics/Arab-Israeli_talk_notice"] = 6100, ["Contentious_topics/Category_database.json"] = 21000, ["Contentious_topics/Restrictions_definition.json"] = 21000, ["Contentious_topics/Standard_set.json"] = 21000, ["Contentious_topics/list"] = 29000, ["Contentious_topics/page_restriction_editnotice_base"] = 7400, ["Contentious_topics/page_restriction_talk_notice"] = 2700, ["Contentious_topics/talk_notice"] = 21000, ["Context"] = 2600, ["Continent2continental"] = 45000, ["Continent_adjective_to_noun"] = 10000, ["Continental2continent"] = 22000, ["Controversial"] = 3000, ["Convert"] = 1420000, ["Convert_abbreviated"] = 171000, ["Convinfobox"] = 308000, ["Coord"] = 1400000, ["Coord_missing"] = 70000, ["Coord_missing/CheckCat"] = 69000, ["Coords"] = 6600, ["Copied"] = 22000, ["Copy_edit"] = 2100, ["Copy_to_Commons"] = 2200, ["Copy_to_Wikimedia_Commons"] = 85000, ["Copy_to_Wikimedia_Commons_in"] = 4300, ["Copyvios"] = 2000, ["Cospar"] = 2800, ["Cot"] = 14000, ["Count"] = 979000, ["Country2continent"] = 89000, ["Country2continental"] = 31000, ["Country2nationality"] = 35000, ["CountryPrefixThe"] = 78000, ["Country_abbreviation"] = 95000, ["Country_alias"] = 17000, ["Country_at_games_navbox"] = 5800, ["Country_at_games_navbox/below"] = 5800, ["Country_data"] = 12000, ["Country_data_AFG"] = 2600, ["Country_data_ALB"] = 7300, ["Country_data_ALG"] = 11000, ["Country_data_AND"] = 3700, ["Country_data_ANG"] = 4400, ["Country_data_ARG"] = 53000, ["Country_data_ARM"] = 8300, ["Country_data_AUS"] = 89000, ["Country_data_AUT"] = 52000, ["Country_data_AZE"] = 11000, ["Country_data_Afghanistan"] = 21000, ["Country_data_Alabama"] = 2600, ["Country_data_Alaska"] = 2100, ["Country_data_Albania"] = 35000, ["Country_data_Alberta"] = 3300, ["Country_data_Algeria"] = 47000, ["Country_data_American_Samoa"] = 2900, ["Country_data_Andorra"] = 9300, ["Country_data_Angola"] = 21000, ["Country_data_Anguilla"] = 2400, ["Country_data_Antigua_and_Barbuda"] = 6400, ["Country_data_Apulia"] = 7900, ["Country_data_Argentina"] = 150000, ["Country_data_Arizona"] = 2500, ["Country_data_Arkansas"] = 2800, ["Country_data_Armenia"] = 46000, ["Country_data_Aruba"] = 4000, ["Country_data_Australia"] = 274000, ["Country_data_Austria"] = 144000, ["Country_data_Austria-Hungary"] = 2300, ["Country_data_Azerbaijan"] = 51000, ["Country_data_BAH"] = 4700, ["Country_data_BAN"] = 4500, ["Country_data_BAR"] = 2700, ["Country_data_BDI"] = 2000, ["Country_data_BEL"] = 60000, ["Country_data_BEN"] = 2000, ["Country_data_BER"] = 2500, ["Country_data_BFA"] = 2000, ["Country_data_BHR"] = 5800, ["Country_data_BIH"] = 14000, ["Country_data_BLR"] = 26000, ["Country_data_BOL"] = 6600, ["Country_data_BOT"] = 3100, ["Country_data_BRA"] = 65000, ["Country_data_BUL"] = 31000, ["Country_data_Bahamas"] = 11000, ["Country_data_Bahrain"] = 21000, ["Country_data_Bangladesh"] = 34000, ["Country_data_Barbados"] = 8800, ["Country_data_Belarus"] = 89000, ["Country_data_Belgium"] = 155000, ["Country_data_Belize"] = 5100, ["Country_data_Benin"] = 8100, ["Country_data_Bermuda"] = 6400, ["Country_data_Bhutan"] = 4900, ["Country_data_Bolivia"] = 24000, ["Country_data_Bosnia_and_Herzegovina"] = 53000, ["Country_data_Botswana"] = 10000, ["Country_data_Brazil"] = 169000, ["Country_data_British_Columbia"] = 3700, ["Country_data_British_Empire"] = 2100, ["Country_data_British_Raj"] = 2500, ["Country_data_British_Virgin_Islands"] = 3400, ["Country_data_Brunei"] = 6900, ["Country_data_Bulgaria"] = 100000, ["Country_data_Burkina_Faso"] = 11000, ["Country_data_Burma"] = 2300, ["Country_data_Burundi"] = 6800, ["Country_data_CAM"] = 2500, ["Country_data_CAN"] = 69000, ["Country_data_CGO"] = 2500, ["Country_data_CHE"] = 5900, ["Country_data_CHI"] = 20000, ["Country_data_CHL"] = 2400, ["Country_data_CHN"] = 49000, ["Country_data_CIV"] = 8600, ["Country_data_CMR"] = 8800, ["Country_data_COD"] = 3500, ["Country_data_COL"] = 28000, ["Country_data_CPV"] = 2300, ["Country_data_CRC"] = 7000, ["Country_data_CRO"] = 37000, ["Country_data_CUB"] = 13000, ["Country_data_CYP"] = 11000, ["Country_data_CZE"] = 54000, ["Country_data_California"] = 6300, ["Country_data_Cambodia"] = 9900, ["Country_data_Cameroon"] = 31000, ["Country_data_Canada"] = 260000, ["Country_data_Cape_Verde"] = 7100, ["Country_data_Castile_and_León"] = 2000, ["Country_data_Catalonia"] = 3400, ["Country_data_Cayman_Islands"] = 4600, ["Country_data_Central_African_Republic"] = 5200, ["Country_data_Chad"] = 5500, ["Country_data_Chile"] = 90000, ["Country_data_China"] = 149000, ["Country_data_Chinese_Taipei"] = 39000, ["Country_data_Colombia"] = 95000, ["Country_data_Colorado"] = 5300, ["Country_data_Commonwealth_of_Independent_States"] = 2200, ["Country_data_Comoros"] = 4700, ["Country_data_Confederate_States_of_America"] = 3100, ["Country_data_Connecticut"] = 3400, ["Country_data_Cook_Islands"] = 3900, ["Country_data_Costa_Rica"] = 33000, ["Country_data_Croatia"] = 104000, ["Country_data_Cuba"] = 46000, ["Country_data_Curaçao"] = 4400, ["Country_data_Cyprus"] = 46000, ["Country_data_Czech_Republic"] = 151000, ["Country_data_Czechoslovakia"] = 34000, ["Country_data_DEN"] = 40000, ["Country_data_DEU"] = 11000, ["Country_data_DNK"] = 4400, ["Country_data_DOM"] = 8400, ["Country_data_DRC"] = 2100, ["Country_data_Delaware"] = 2700, ["Country_data_Democratic_Republic_of_the_Congo"] = 22000, ["Country_data_Denmark"] = 120000, ["Country_data_Djibouti"] = 5000, ["Country_data_Dominica"] = 4200, ["Country_data_Dominican_Republic"] = 34000, ["Country_data_ECU"] = 13000, ["Country_data_EGY"] = 16000, ["Country_data_ENG"] = 52000, ["Country_data_ESA"] = 2500, ["Country_data_ESP"] = 85000, ["Country_data_EST"] = 17000, ["Country_data_ETH"] = 4600, ["Country_data_EU"] = 2600, ["Country_data_East_Germany"] = 28000, ["Country_data_Ecuador"] = 48000, ["Country_data_Egypt"] = 65000, ["Country_data_El_Salvador"] = 22000, ["Country_data_Empire_of_Japan"] = 4400, ["Country_data_England"] = 162000, ["Country_data_Equatorial_Guinea"] = 5500, ["Country_data_Eritrea"] = 5500, ["Country_data_Estonia"] = 68000, ["Country_data_Eswatini"] = 5100, ["Country_data_Ethiopia"] = 21000, ["Country_data_Europe"] = 2800, ["Country_data_European_Union"] = 8100, ["Country_data_FIJ"] = 4500, ["Country_data_FIN"] = 40000, ["Country_data_FRA"] = 115000, ["Country_data_FRG"] = 18000, ["Country_data_FRO"] = 2200, ["Country_data_FR_Yugoslavia"] = 4400, ["Country_data_Faroe_Islands"] = 5800, ["Country_data_Federated_States_of_Micronesia"] = 2700, ["Country_data_Fiji"] = 21000, ["Country_data_Finland"] = 113000, ["Country_data_Florida"] = 5800, ["Country_data_France"] = 333000, ["Country_data_French_Polynesia"] = 3900, ["Country_data_GAB"] = 2500, ["Country_data_GAM"] = 2300, ["Country_data_GBR"] = 67000, ["Country_data_GDR"] = 11000, ["Country_data_GEO"] = 16000, ["Country_data_GER"] = 95000, ["Country_data_GHA"] = 10000, ["Country_data_GIB"] = 2100, ["Country_data_GRE"] = 30000, ["Country_data_GUA"] = 5400, ["Country_data_GUI"] = 3400, ["Country_data_GUY"] = 2600, ["Country_data_Gabon"] = 8100, ["Country_data_Gambia"] = 7600, ["Country_data_Georgia"] = 8600, ["Country_data_Georgia_(U.S._state)"] = 3200, ["Country_data_Georgia_(country)"] = 53000, ["Country_data_German_Empire"] = 5700, ["Country_data_Germany"] = 291000, ["Country_data_Ghana"] = 46000, ["Country_data_Gibraltar"] = 5200, ["Country_data_Great_Britain"] = 127000, ["Country_data_Greece"] = 105000, ["Country_data_Greenland"] = 2800, ["Country_data_Grenada"] = 5700, ["Country_data_Guadeloupe"] = 2800, ["Country_data_Guam"] = 4600, ["Country_data_Guatemala"] = 22000, ["Country_data_Guinea"] = 9400, ["Country_data_Guinea-Bissau"] = 5500, ["Country_data_Guyana"] = 7700, ["Country_data_HAI"] = 3300, ["Country_data_HKG"] = 15000, ["Country_data_HON"] = 4400, ["Country_data_HUN"] = 45000, ["Country_data_Haiti"] = 9800, ["Country_data_Hawaii"] = 2200, ["Country_data_Honduras"] = 21000, ["Country_data_Hong_Kong"] = 51000, ["Country_data_Hungary"] = 114000, ["Country_data_IDN"] = 5800, ["Country_data_INA"] = 12000, ["Country_data_IND"] = 34000, ["Country_data_IRE"] = 12000, ["Country_data_IRI"] = 6700, ["Country_data_IRL"] = 24000, ["Country_data_IRN"] = 7300, ["Country_data_IRQ"] = 4800, ["Country_data_ISL"] = 9300, ["Country_data_ISR"] = 23000, ["Country_data_ITA"] = 102000, ["Country_data_Iceland"] = 46000, ["Country_data_Idaho"] = 2200, ["Country_data_Illinois"] = 4400, ["Country_data_India"] = 257000, ["Country_data_Indiana"] = 3400, ["Country_data_Indonesia"] = 71000, ["Country_data_Iowa"] = 2400, ["Country_data_Iran"] = 159000, ["Country_data_Iraq"] = 23000, ["Country_data_Ireland"] = 66000, ["Country_data_Isle_of_Man"] = 2900, ["Country_data_Israel"] = 92000, ["Country_data_Italy"] = 286000, ["Country_data_Ivory_Coast"] = 32000, ["Country_data_JAM"] = 12000, ["Country_data_JAP"] = 2300, ["Country_data_JOR"] = 4800, ["Country_data_JP"] = 8700, ["Country_data_JPN"] = 69000, ["Country_data_Jamaica"] = 44000, ["Country_data_Japan"] = 263000, ["Country_data_Jersey"] = 2500, ["Country_data_Jordan"] = 22000, ["Country_data_KAZ"] = 24000, ["Country_data_KEN"] = 10000, ["Country_data_KGZ"] = 4500, ["Country_data_KOR"] = 35000, ["Country_data_KOS"] = 3100, ["Country_data_KSA"] = 7500, ["Country_data_KUW"] = 4600, ["Country_data_Kazakhstan"] = 67000, ["Country_data_Kentucky"] = 2100, ["Country_data_Kenya"] = 35000, ["Country_data_Kingdom_of_France"] = 2200, ["Country_data_Kingdom_of_Great_Britain"] = 4600, ["Country_data_Kingdom_of_Italy"] = 4800, ["Country_data_Kiribati"] = 2600, ["Country_data_Kosovo"] = 10000, ["Country_data_Kuwait"] = 20000, ["Country_data_Kyrgyzstan"] = 11000, ["Country_data_LAO"] = 2200, ["Country_data_LAT"] = 17000, ["Country_data_LBN"] = 2900, ["Country_data_LBR"] = 2000, ["Country_data_LIB"] = 2600, ["Country_data_LIE"] = 3800, ["Country_data_LIT"] = 3700, ["Country_data_LTU"] = 15000, ["Country_data_LUX"] = 12000, ["Country_data_LVA"] = 3000, ["Country_data_Laos"] = 8000, ["Country_data_Latvia"] = 54000, ["Country_data_Lebanon"] = 23000, ["Country_data_Lesotho"] = 5400, ["Country_data_Liberia"] = 7900, ["Country_data_Libya"] = 9100, ["Country_data_Liechtenstein"] = 8900, ["Country_data_Lithuania"] = 54000, ["Country_data_Louisiana"] = 2300, ["Country_data_Luxembourg"] = 48000, ["Country_data_MAC"] = 3000, ["Country_data_MAD"] = 2400, ["Country_data_MAR"] = 15000, ["Country_data_MAS"] = 13000, ["Country_data_MDA"] = 8900, ["Country_data_MEX"] = 35000, ["Country_data_MGL"] = 3600, ["Country_data_MKD"] = 8500, ["Country_data_MLI"] = 4600, ["Country_data_MLT"] = 6500, ["Country_data_MNE"] = 8600, ["Country_data_MON"] = 4700, ["Country_data_MOZ"] = 2700, ["Country_data_MRI"] = 2200, ["Country_data_MYA"] = 3400, ["Country_data_MYS"] = 4200, ["Country_data_Macau"] = 6900, ["Country_data_Macedonia"] = 4600, ["Country_data_Madagascar"] = 9600, ["Country_data_Malawi"] = 6200, ["Country_data_Malaysia"] = 71000, ["Country_data_Maldives"] = 6700, ["Country_data_Mali"] = 21000, ["Country_data_Malta"] = 31000, ["Country_data_Manitoba"] = 3000, ["Country_data_Marshall_Islands"] = 3400, ["Country_data_Martinique"] = 2700, ["Country_data_Maryland"] = 3300, ["Country_data_Massachusetts"] = 2600, ["Country_data_Mauritania"] = 6000, ["Country_data_Mauritius"] = 8500, ["Country_data_Mexico"] = 114000, ["Country_data_Michigan"] = 3800, ["Country_data_Minas_Gerais"] = 2100, ["Country_data_Minnesota"] = 3700, ["Country_data_Missouri"] = 2500, ["Country_data_Moldova"] = 33000, ["Country_data_Monaco"] = 12000, ["Country_data_Mongolia"] = 11000, ["Country_data_Montana"] = 2400, ["Country_data_Montenegro"] = 33000, ["Country_data_Montserrat"] = 2400, ["Country_data_Morocco"] = 51000, ["Country_data_Mozambique"] = 8000, ["Country_data_Myanmar"] = 23000, ["Country_data_NAM"] = 4000, ["Country_data_NED"] = 72000, ["Country_data_NEP"] = 3400, ["Country_data_NGA"] = 8700, ["Country_data_NGR"] = 9400, ["Country_data_NIR"] = 11000, ["Country_data_NLD"] = 7400, ["Country_data_NOR"] = 35000, ["Country_data_NZ"] = 3700, ["Country_data_NZL"] = 39000, ["Country_data_Namibia"] = 11000, ["Country_data_Nauru"] = 2600, ["Country_data_Nazi_Germany"] = 10000, ["Country_data_Nepal"] = 31000, ["Country_data_Netherlands"] = 260000, ["Country_data_Netherlands_Antilles"] = 2400, ["Country_data_Nevada"] = 2100, ["Country_data_New_Brunswick"] = 2600, ["Country_data_New_Caledonia"] = 3300, ["Country_data_New_Hampshire"] = 2000, ["Country_data_New_Jersey"] = 4200, ["Country_data_New_South_Wales"] = 6400, ["Country_data_New_York"] = 4300, ["Country_data_New_York_(state)"] = 6800, ["Country_data_New_Zealand"] = 118000, ["Country_data_Newfoundland_and_Labrador"] = 2800, ["Country_data_Nicaragua"] = 9000, ["Country_data_Niger"] = 6300, ["Country_data_Nigeria"] = 66000, ["Country_data_North_Carolina"] = 3500, ["Country_data_North_Korea"] = 23000, ["Country_data_North_Macedonia"] = 33000, ["Country_data_Northern_Ireland"] = 27000, ["Country_data_Northern_Mariana_Islands"] = 2800, ["Country_data_Norway"] = 116000, ["Country_data_Nova_Scotia"] = 2800, ["Country_data_OMA"] = 3300, ["Country_data_Ohio"] = 5800, ["Country_data_Oklahoma"] = 2500, ["Country_data_Oman"] = 10000, ["Country_data_Ontario"] = 3900, ["Country_data_Ottoman_Empire"] = 3200, ["Country_data_PAK"] = 9000, ["Country_data_PAN"] = 6000, ["Country_data_PAR"] = 11000, ["Country_data_PER"] = 14000, ["Country_data_PHI"] = 13000, ["Country_data_PLE"] = 2000, ["Country_data_PNG"] = 3200, ["Country_data_POL"] = 61000, ["Country_data_POR"] = 37000, ["Country_data_PRK"] = 5000, ["Country_data_PRT"] = 3600, ["Country_data_PUR"] = 8100, ["Country_data_Pakistan"] = 51000, ["Country_data_Palau"] = 2600, ["Country_data_Palestine"] = 7700, ["Country_data_Panama"] = 30000, ["Country_data_Papua_New_Guinea"] = 8400, ["Country_data_Paraguay"] = 44000, ["Country_data_Pennsylvania"] = 3600, ["Country_data_Peru"] = 64000, ["Country_data_Philippines"] = 68000, ["Country_data_Poland"] = 240000, ["Country_data_Portugal"] = 116000, ["Country_data_Prussia"] = 2800, ["Country_data_Puerto_Rico"] = 34000, ["Country_data_QAT"] = 9500, ["Country_data_Qatar"] = 32000, ["Country_data_Quebec"] = 4400, ["Country_data_Queensland"] = 2300, ["Country_data_ROM"] = 15000, ["Country_data_ROU"] = 31000, ["Country_data_RSA"] = 36000, ["Country_data_RUS"] = 68000, ["Country_data_RWA"] = 2500, ["Country_data_Republic_of_China"] = 4600, ["Country_data_Republic_of_Ireland"] = 54000, ["Country_data_Republic_of_the_Congo"] = 8300, ["Country_data_Romania"] = 113000, ["Country_data_Russia"] = 253000, ["Country_data_Russian_Empire"] = 5600, ["Country_data_Rwanda"] = 8500, ["Country_data_SAM"] = 3700, ["Country_data_SCG"] = 3000, ["Country_data_SCO"] = 29000, ["Country_data_SEN"] = 8500, ["Country_data_SER"] = 3700, ["Country_data_SGP"] = 4500, ["Country_data_SIN"] = 7400, ["Country_data_SLE"] = 2000, ["Country_data_SLO"] = 22000, ["Country_data_SLV"] = 3200, ["Country_data_SMR"] = 3600, ["Country_data_SPA"] = 5500, ["Country_data_SRB"] = 30000, ["Country_data_SRI"] = 5300, ["Country_data_SUI"] = 50000, ["Country_data_SUR"] = 2300, ["Country_data_SVK"] = 32000, ["Country_data_SVN"] = 7300, ["Country_data_SWE"] = 65000, ["Country_data_SWI"] = 5100, ["Country_data_SYR"] = 3900, ["Country_data_Saint_Kitts_and_Nevis"] = 4900, ["Country_data_Saint_Lucia"] = 5300, ["Country_data_Saint_Vincent_and_the_Grenadines"] = 5000, ["Country_data_Samoa"] = 8000, ["Country_data_San_Marino"] = 9600, ["Country_data_Saskatchewan"] = 4000, ["Country_data_Saudi_Arabia"] = 37000, ["Country_data_Scotland"] = 101000, ["Country_data_Senegal"] = 31000, ["Country_data_Serbia"] = 103000, ["Country_data_Serbia_and_Montenegro"] = 5400, ["Country_data_Seychelles"] = 5800, ["Country_data_Sierra_Leone"] = 7600, ["Country_data_Singapore"] = 50000, ["Country_data_Slovakia"] = 95000, ["Country_data_Slovenia"] = 90000, ["Country_data_Solomon_Islands"] = 4700, ["Country_data_Somalia"] = 6400, ["Country_data_South_Africa"] = 120000, ["Country_data_South_Australia"] = 2300, ["Country_data_South_Carolina"] = 3100, ["Country_data_South_Korea"] = 115000, ["Country_data_South_Sudan"] = 4800, ["Country_data_Soviet_Union"] = 76000, ["Country_data_Spain"] = 275000, ["Country_data_Sri_Lanka"] = 35000, ["Country_data_Sudan"] = 8700, ["Country_data_Suriname"] = 6800, ["Country_data_Sweden"] = 165000, ["Country_data_Switzerland"] = 149000, ["Country_data_Syria"] = 21000, ["Country_data_São_Tomé_and_Príncipe"] = 3400, ["Country_data_TAN"] = 3500, ["Country_data_TCH"] = 12000, ["Country_data_THA"] = 25000, ["Country_data_TJK"] = 2900, ["Country_data_TKM"] = 3100, ["Country_data_TOG"] = 2100, ["Country_data_TON"] = 2000, ["Country_data_TPE"] = 19000, ["Country_data_TRI"] = 5100, ["Country_data_TTO"] = 2200, ["Country_data_TUN"] = 12000, ["Country_data_TUR"] = 33000, ["Country_data_TWN"] = 2200, ["Country_data_Taiwan"] = 27000, ["Country_data_Tajikistan"] = 9700, ["Country_data_Tanzania"] = 22000, ["Country_data_Tennessee"] = 2200, ["Country_data_Texas"] = 5800, ["Country_data_Thailand"] = 95000, ["Country_data_Timor-Leste"] = 5100, ["Country_data_Togo"] = 7600, ["Country_data_Tonga"] = 6700, ["Country_data_Trinidad_and_Tobago"] = 24000, ["Country_data_Tunisia"] = 45000, ["Country_data_Turkey"] = 139000, ["Country_data_Turkmenistan"] = 8400, ["Country_data_Turks_and_Caicos_Islands"] = 2800, ["Country_data_Tuvalu"] = 2600, ["Country_data_U.S."] = 2000, ["Country_data_U.S._Virgin_Islands"] = 4900, ["Country_data_UAE"] = 11000, ["Country_data_UGA"] = 5300, ["Country_data_UK"] = 19000, ["Country_data_UKGBI"] = 3500, ["Country_data_UKR"] = 44000, ["Country_data_URS"] = 19000, ["Country_data_URU"] = 16000, ["Country_data_US"] = 5700, ["Country_data_USA"] = 151000, ["Country_data_USSR"] = 5300, ["Country_data_UZB"] = 14000, ["Country_data_Uganda"] = 22000, ["Country_data_Ukraine"] = 118000, ["Country_data_United_Arab_Emirates"] = 38000, ["Country_data_United_Kingdom"] = 157000, ["Country_data_United_Kingdom_of_Great_Britain_and_Ireland"] = 4900, ["Country_data_United_Nations"] = 4400, ["Country_data_United_States"] = 434000, ["Country_data_United_States_of_America"] = 4500, ["Country_data_Uruguay"] = 52000, ["Country_data_Uzbekistan"] = 36000, ["Country_data_VEN"] = 18000, ["Country_data_VIE"] = 7300, ["Country_data_Vanuatu"] = 5100, ["Country_data_Vatican_City"] = 2900, ["Country_data_Venezuela"] = 65000, ["Country_data_Victoria"] = 2400, ["Country_data_Vietnam"] = 46000, ["Country_data_Virginia"] = 3100, ["Country_data_WAL"] = 19000, ["Country_data_Wales"] = 67000, ["Country_data_Washington"] = 2700, ["Country_data_Washington,_D.C."] = 2200, ["Country_data_Washington_(state)"] = 3400, ["Country_data_West_Germany"] = 52000, ["Country_data_West_Indies"] = 2800, ["Country_data_Wisconsin"] = 5600, ["Country_data_Wyoming"] = 2100, ["Country_data_YUG"] = 11000, ["Country_data_Yemen"] = 8000, ["Country_data_Yugoslavia"] = 34000, ["Country_data_ZAF"] = 5400, ["Country_data_ZAM"] = 3800, ["Country_data_ZIM"] = 9200, ["Country_data_Zambia"] = 11000, ["Country_data_Zimbabwe"] = 32000, ["Country_data_none"] = 2100, ["Country_flagbio"] = 40000, ["Country_in_the_Eurovision_Song_Contest"] = 2900, ["Country_name"] = 59000, ["Country_prefix_the"] = 176000, ["Country_showdata"] = 7000, ["Country_topics"] = 25000, ["County"] = 7000, ["County_(judet)_of_Romania"] = 3300, ["Course_assignment"] = 4200, ["Course_details"] = 8500, ["Course_instructor"] = 3200, ["Cquote"] = 37000, ["Cr"] = 5000, ["Cr-rt"] = 2400, ["Create_taxonomy/link"] = 150000, ["Creative_Commons_text_attribution_notice"] = 6300, ["Cref2"] = 2900, ["Cricinfo"] = 24000, ["Cricket_squad2_player"] = 2700, ["Cricketarchive"] = 3000, ["Croatian_Census_2021"] = 4200, ["Cross"] = 4100, ["Crossreference"] = 2600, ["Crossreference/styles.css"] = 2600, ["Cs1_config"] = 13000, ["Cslist"] = 3700, ["Csv"] = 76000, ["Currency"] = 5400, ["Current_events"] = 9400, ["Current_events/styles.css"] = 9400, ["Current_leader"] = 5100, ["Currentdate"] = 24000, ["Cvt"] = 171000, ["Cycling_Archives"] = 4300, ["Cycling_data_DAT"] = 2000, ["Cycling_data_LTD"] = 2400, ["Cycling_data_MOV_men"] = 2300, ["Cycling_data_SOQ"] = 2200, ["Cycling_data_TVL"] = 2400, ["Cycling_data_UEX"] = 2300, ["Cycling_team_link"] = 14000, ["Module:CFB_schedule"] = 29000, ["Module:CS1_identifiers"] = 507000, ["Module:CallAssert"] = 122000, ["Module:Canada_NTS"] = 2100, ["Module:Canada_NTS/data"] = 2100, ["Module:Canada_by_province_category_navigation"] = 9900, ["Module:Catalog_lookup_link"] = 61000, ["Module:Category_described_in_year"] = 6400, ["Module:Category_described_in_year/config"] = 6400, ["Module:Category_disambiguation"] = 2700, ["Module:Category_handler"] = 4990000, ["Module:Category_handler/blacklist"] = 4990000, ["Module:Category_handler/config"] = 4990000, ["Module:Category_handler/data"] = 4990000, ["Module:Category_handler/shared"] = 4990000, ["Module:Category_main_article"] = 221000, ["Module:Category_more_if_exists"] = 49000, ["Module:Category_pair"] = 7100, ["Module:Category_see_also"] = 55000, ["Module:Category_series_navigation"] = 598000, ["Module:Category_series_navigation/styles.css"] = 598000, ["Module:Celestial_object_quadrangle"] = 2100, ["Module:Chart"] = 2900, ["Module:Chart/Default_colors"] = 2900, ["Module:Check_DYK_hook"] = 126000, ["Module:Check_blp_parameter"] = 2910000, ["Module:Check_for_conflicting_parameters"] = 4730000, ["Module:Check_for_deprecated_parameters"] = 989000, ["Module:Check_for_unknown_parameters"] = 23400000, ["Module:Check_height"] = 153000, ["Module:Check_isxn"] = 16000, ["Module:Check_winner_by_scores"] = 16000, ["Module:Checkuser"] = 90000, ["Module:Chem2"] = 11000, ["Module:Chem2/styles.css"] = 11000, ["Module:Citation/CS1"] = 6540000, ["Module:Citation/CS1/COinS"] = 6540000, ["Module:Citation/CS1/Configuration"] = 6670000, ["Module:Citation/CS1/Date_validation"] = 6540000, ["Module:Citation/CS1/Identifiers"] = 6660000, ["Module:Citation/CS1/Suggestions"] = 26000, ["Module:Citation/CS1/Utilities"] = 6670000, ["Module:Citation/CS1/Whitelist"] = 6540000, ["Module:Citation/CS1/styles.css"] = 6670000, ["Module:Citation_mode"] = 49000, ["Module:Cite"] = 53000, ["Module:Cite/config"] = 70000, ["Module:Cite_IUCN"] = 71000, ["Module:Cite_Q"] = 17000, ["Module:Cite_Q/config"] = 17000, ["Module:Cite_taxon"] = 55000, ["Module:Cite_tweet"] = 48000, ["Module:Clade"] = 10000, ["Module:Class"] = 62000, ["Module:Class/definition.json"] = 62000, ["Module:Class/styles.css"] = 39000, ["Module:Class_mask"] = 461000, ["Module:Clickable_button"] = 1870000, ["Module:Collapsible_list"] = 70000, ["Module:College_color"] = 137000, ["Module:College_color/data"] = 137000, ["Module:Color_contrast"] = 575000, ["Module:Color_contrast/colors"] = 578000, ["Module:Comma-separated_list"] = 3700, ["Module:Commons_link"] = 856000, ["Module:Compact_list"] = 2000, ["Module:Complex_date"] = 47000, ["Module:Conservation_status"] = 89000, ["Module:Contentious_topics/talk_notice"] = 21000, ["Module:Convert"] = 1510000, ["Module:Convert/data"] = 1510000, ["Module:Convert/helper"] = 13000, ["Module:Convert/text"] = 1510000, ["Module:Convert/wikidata"] = 2700, ["Module:Convert/wikidata/data"] = 2700, ["Module:ConvertIB"] = 799000, ["Module:ConvertIB/data"] = 274000, ["Module:ConvertNumeric"] = 21000, ["Module:Convert_character_width"] = 2700, ["Module:Convert_character_width/data"] = 2700, ["Module:Coordinates"] = 1410000, ["Module:Coordinates/styles.css"] = 1410000, ["Module:Copied"] = 43000, ["Module:Country2nationality"] = 97000, ["Module:CountryAdjectiveDemonym"] = 116000, ["Module:CountryAdjectiveDemonym/Adjectives"] = 116000, ["Module:CountryAdjectiveDemonym/Demonyms"] = 116000, ["Module:CountryAdjectiveDemonym/The"] = 116000, ["Module:CountryData"] = 475000, ["Module:CountryData/cacheA"] = 165000, ["Module:CountryData/cacheB"] = 81000, ["Module:CountryData/cacheC"] = 59000, ["Module:CountryData/cacheD"] = 56000, ["Module:CountryData/cacheE"] = 38000, ["Module:CountryData/cacheF"] = 26000, ["Module:CountryData/cacheG"] = 27000, ["Module:CountryData/cacheH"] = 18000, ["Module:CountryData/cacheI"] = 17000, ["Module:CountryData/cacheJ"] = 11000, ["Module:CountryData/cacheK"] = 11000, ["Module:CountryData/summary"] = 475000, ["Module:Country_adjective"] = 4700, ["Module:Country_alias"] = 62000, ["Module:Country_alias/data"] = 62000, ["Module:Cslist"] = 3700, ["Module:Currency"] = 5500, ["Module:Currency/Presentation"] = 5500, ["Module:Current_leader"] = 5300, ["Module:Current_leader/data"] = 5300, } n1wqoeq6ajuyzaw157ew09067nsjvd9 ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane 3 15786 366149 365938 2026-08-21T08:05:47Z ChiK 1136 /* August 2026 */ fixed broken link 366149 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) 3vh9fzvvsjgpzqhhlw0upmlm827hi7g 366157 366149 2026-08-21T08:40:23Z Shreelatha.Halemane 6351 /* t */ಪೊಸ ವಿಭಾಗ 366157 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ == ಸರ್ ನಮಸ್ತೆ, ನಾನು ಬರೆದಿರುವ ಅರ್ಟಿಕಲ್ನಲ್ಲಿ ಎನಾದರೂ ತಪ್ಪುಗಳಿದ್ದಲ್ಲಿ ದಯವಿಟ್ಟು ತಿಳಿಸಿ. ಸರಿಯಾಗಿ ಯಾವ ಆರ್ಟಿಕಲ್ ಮತ್ತು ಎಲ್ಲಿ ಯಾವ ವಾಕ್ಯ ಸರಿಪಡಿಸಬೇಕೆಂಬುದನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ತಿಳಿಸಿ.ಖಂಡಿತವಾಗಿಯೂ ಸರಿಪಡಿಸುತ್ತೇನೆ...ನನ್ನ ಮಾತೃ ಭಾಷೆ ಕನ್ನಡ ಆದ್ದರಿಂದ ತುಳುವಿನಲ್ಲಿ ಗೊತ್ತಿರುವ ರೀತಿಯಲ್ಲಿ ಅರ್ಟಿಕಲ್ ಮಾಡುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೪:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) lv5er3p1y58pr6gek5u7owi9br3ww9g 366163 366157 2026-08-21T09:25:48Z Mahaveer Indra 1023 /* ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ */ ಪ್ರತಿಕ್ರಿಯೆ 366163 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ == ಸರ್ ನಮಸ್ತೆ, ನಾನು ಬರೆದಿರುವ ಅರ್ಟಿಕಲ್ನಲ್ಲಿ ಎನಾದರೂ ತಪ್ಪುಗಳಿದ್ದಲ್ಲಿ ದಯವಿಟ್ಟು ತಿಳಿಸಿ. ಸರಿಯಾಗಿ ಯಾವ ಆರ್ಟಿಕಲ್ ಮತ್ತು ಎಲ್ಲಿ ಯಾವ ವಾಕ್ಯ ಸರಿಪಡಿಸಬೇಕೆಂಬುದನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ತಿಳಿಸಿ.ಖಂಡಿತವಾಗಿಯೂ ಸರಿಪಡಿಸುತ್ತೇನೆ...ನನ್ನ ಮಾತೃ ಭಾಷೆ ಕನ್ನಡ ಆದ್ದರಿಂದ ತುಳುವಿನಲ್ಲಿ ಗೊತ್ತಿರುವ ರೀತಿಯಲ್ಲಿ ಅರ್ಟಿಕಲ್ ಮಾಡುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೪:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :ನಮಸ್ಕಾರ. :ಲೇಖನವನ್ನು ಯಾವುದೇ ಭಾಷೆಯಿಂದ ತುಳುವಿಗೆ ಅಥವಾ ಕನ್ನಡಕ್ಕೆ ಅನುವಾದಿಸುವಾಗ ಓದುಗರಿಗೆ ಅರ್ಥವಾಗಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ನಿಮ್ಮ ಧೋಲಾವಿರ ಬರಹದಲ್ಲಿ ಇರುವುದು ಇದೇ ಸಮಸ್ಯೆ- ನನ್ನ ಪ್ರತಿಕ್ರಿಯೆ ಈ ಲೇಖನಕ್ಕೆ ಮಾತ್ರ, ಬೇರೆ ಲೇಖನಗಳನ್ನು ನನು ಗಮನಿಸಿಲ್ಲ- ಬರೆದ ಲೇಖನವು ಅರ್ಥಬದ್ಧವಾಗಿ ಇರಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ಸರಳವಾಗಿ ಎಲ್ಲರಿಗೂ ಅರ್ಥವಾಗುವ ಹಾಗೆ ಬರೆಯಿರಿ ಎಂಬ ವಿನಂತಿ. [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) kuol4hyjbztcuvauf1drvqwsl5jjqy6 366164 366163 2026-08-21T09:31:29Z Shreelatha.Halemane 6351 /* ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ */ ಪ್ರತಿಕ್ರಿಯೆ 366164 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ == ಸರ್ ನಮಸ್ತೆ, ನಾನು ಬರೆದಿರುವ ಅರ್ಟಿಕಲ್ನಲ್ಲಿ ಎನಾದರೂ ತಪ್ಪುಗಳಿದ್ದಲ್ಲಿ ದಯವಿಟ್ಟು ತಿಳಿಸಿ. ಸರಿಯಾಗಿ ಯಾವ ಆರ್ಟಿಕಲ್ ಮತ್ತು ಎಲ್ಲಿ ಯಾವ ವಾಕ್ಯ ಸರಿಪಡಿಸಬೇಕೆಂಬುದನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ತಿಳಿಸಿ.ಖಂಡಿತವಾಗಿಯೂ ಸರಿಪಡಿಸುತ್ತೇನೆ...ನನ್ನ ಮಾತೃ ಭಾಷೆ ಕನ್ನಡ ಆದ್ದರಿಂದ ತುಳುವಿನಲ್ಲಿ ಗೊತ್ತಿರುವ ರೀತಿಯಲ್ಲಿ ಅರ್ಟಿಕಲ್ ಮಾಡುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೪:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :ನಮಸ್ಕಾರ. :ಲೇಖನವನ್ನು ಯಾವುದೇ ಭಾಷೆಯಿಂದ ತುಳುವಿಗೆ ಅಥವಾ ಕನ್ನಡಕ್ಕೆ ಅನುವಾದಿಸುವಾಗ ಓದುಗರಿಗೆ ಅರ್ಥವಾಗಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ನಿಮ್ಮ ಧೋಲಾವಿರ ಬರಹದಲ್ಲಿ ಇರುವುದು ಇದೇ ಸಮಸ್ಯೆ- ನನ್ನ ಪ್ರತಿಕ್ರಿಯೆ ಈ ಲೇಖನಕ್ಕೆ ಮಾತ್ರ, ಬೇರೆ ಲೇಖನಗಳನ್ನು ನನು ಗಮನಿಸಿಲ್ಲ- ಬರೆದ ಲೇಖನವು ಅರ್ಥಬದ್ಧವಾಗಿ ಇರಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ಸರಳವಾಗಿ ಎಲ್ಲರಿಗೂ ಅರ್ಥವಾಗುವ ಹಾಗೆ ಬರೆಯಿರಿ ಎಂಬ ವಿನಂತಿ. [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::ಸರಿಪಡಿಸಿ ಬರೆಯುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೧, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) nbxfq9t15pinu95frabqgql5sckcoo0 366165 366164 2026-08-21T09:35:28Z Mahaveer Indra 1023 /* ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ */ ಪ್ರತಿಕ್ರಿಯೆ 366165 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ == ಸರ್ ನಮಸ್ತೆ, ನಾನು ಬರೆದಿರುವ ಅರ್ಟಿಕಲ್ನಲ್ಲಿ ಎನಾದರೂ ತಪ್ಪುಗಳಿದ್ದಲ್ಲಿ ದಯವಿಟ್ಟು ತಿಳಿಸಿ. ಸರಿಯಾಗಿ ಯಾವ ಆರ್ಟಿಕಲ್ ಮತ್ತು ಎಲ್ಲಿ ಯಾವ ವಾಕ್ಯ ಸರಿಪಡಿಸಬೇಕೆಂಬುದನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ತಿಳಿಸಿ.ಖಂಡಿತವಾಗಿಯೂ ಸರಿಪಡಿಸುತ್ತೇನೆ...ನನ್ನ ಮಾತೃ ಭಾಷೆ ಕನ್ನಡ ಆದ್ದರಿಂದ ತುಳುವಿನಲ್ಲಿ ಗೊತ್ತಿರುವ ರೀತಿಯಲ್ಲಿ ಅರ್ಟಿಕಲ್ ಮಾಡುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೪:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :ನಮಸ್ಕಾರ. :ಲೇಖನವನ್ನು ಯಾವುದೇ ಭಾಷೆಯಿಂದ ತುಳುವಿಗೆ ಅಥವಾ ಕನ್ನಡಕ್ಕೆ ಅನುವಾದಿಸುವಾಗ ಓದುಗರಿಗೆ ಅರ್ಥವಾಗಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ನಿಮ್ಮ ಧೋಲಾವಿರ ಬರಹದಲ್ಲಿ ಇರುವುದು ಇದೇ ಸಮಸ್ಯೆ- ನನ್ನ ಪ್ರತಿಕ್ರಿಯೆ ಈ ಲೇಖನಕ್ಕೆ ಮಾತ್ರ, ಬೇರೆ ಲೇಖನಗಳನ್ನು ನನು ಗಮನಿಸಿಲ್ಲ- ಬರೆದ ಲೇಖನವು ಅರ್ಥಬದ್ಧವಾಗಿ ಇರಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ಸರಳವಾಗಿ ಎಲ್ಲರಿಗೂ ಅರ್ಥವಾಗುವ ಹಾಗೆ ಬರೆಯಿರಿ ಎಂಬ ವಿನಂತಿ. [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::ಸರಿಪಡಿಸಿ ಬರೆಯುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೧, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :::ಸರಿ. ಬೇರೆ ವಿಕಿಗೆ ಹೋಲಿಸಿದಾಗ ತುಳು ಮತ್ತು ಕನ್ನಡ ವಿಕಿಯಲ್ಲಿ ಬರಹಗಳ ಸಂಖ್ಯೆ ಕಡಿಮೆ ಇದೆ. ಆದರೆ ಅದೇ ಗುಣಮಟ್ಟದ ವಿಷಯಕ್ಕೆ ಬಂದಾಗ ಕನ್ನಡ ಮತ್ತು ತುಳು ವಿಕಿ ಮುಂಚೂಣಿಯಲ್ಲಿ ನಿಲ್ಲುತ್ತವೆ. ವಿಕಿ ಅಡ್ಮಿನ್ ನಿಮಗೆ ಸೂಚನೆ ಕೊಟ್ಟದ್ದರ ಉದ್ದೇಶ ಇದೇ ಆಗಿದೆ. ಧನ್ಯವಾದಗಳು [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) 4bfwwm4s2xwe5fwl1elkzqfzhyvai3y 366172 366165 2026-08-21T09:48:13Z Kavitha G. Kana 1040 /* ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ */ ಪ್ರತಿಕ್ರಿಯೆ 366172 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ == ಸರ್ ನಮಸ್ತೆ, ನಾನು ಬರೆದಿರುವ ಅರ್ಟಿಕಲ್ನಲ್ಲಿ ಎನಾದರೂ ತಪ್ಪುಗಳಿದ್ದಲ್ಲಿ ದಯವಿಟ್ಟು ತಿಳಿಸಿ. ಸರಿಯಾಗಿ ಯಾವ ಆರ್ಟಿಕಲ್ ಮತ್ತು ಎಲ್ಲಿ ಯಾವ ವಾಕ್ಯ ಸರಿಪಡಿಸಬೇಕೆಂಬುದನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ತಿಳಿಸಿ.ಖಂಡಿತವಾಗಿಯೂ ಸರಿಪಡಿಸುತ್ತೇನೆ...ನನ್ನ ಮಾತೃ ಭಾಷೆ ಕನ್ನಡ ಆದ್ದರಿಂದ ತುಳುವಿನಲ್ಲಿ ಗೊತ್ತಿರುವ ರೀತಿಯಲ್ಲಿ ಅರ್ಟಿಕಲ್ ಮಾಡುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೪:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :ನಮಸ್ಕಾರ. :ಲೇಖನವನ್ನು ಯಾವುದೇ ಭಾಷೆಯಿಂದ ತುಳುವಿಗೆ ಅಥವಾ ಕನ್ನಡಕ್ಕೆ ಅನುವಾದಿಸುವಾಗ ಓದುಗರಿಗೆ ಅರ್ಥವಾಗಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ನಿಮ್ಮ ಧೋಲಾವಿರ ಬರಹದಲ್ಲಿ ಇರುವುದು ಇದೇ ಸಮಸ್ಯೆ- ನನ್ನ ಪ್ರತಿಕ್ರಿಯೆ ಈ ಲೇಖನಕ್ಕೆ ಮಾತ್ರ, ಬೇರೆ ಲೇಖನಗಳನ್ನು ನನು ಗಮನಿಸಿಲ್ಲ- ಬರೆದ ಲೇಖನವು ಅರ್ಥಬದ್ಧವಾಗಿ ಇರಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ಸರಳವಾಗಿ ಎಲ್ಲರಿಗೂ ಅರ್ಥವಾಗುವ ಹಾಗೆ ಬರೆಯಿರಿ ಎಂಬ ವಿನಂತಿ. [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::ಸರಿಪಡಿಸಿ ಬರೆಯುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೧, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :::ಸರಿ. ಬೇರೆ ವಿಕಿಗೆ ಹೋಲಿಸಿದಾಗ ತುಳು ಮತ್ತು ಕನ್ನಡ ವಿಕಿಯಲ್ಲಿ ಬರಹಗಳ ಸಂಖ್ಯೆ ಕಡಿಮೆ ಇದೆ. ಆದರೆ ಅದೇ ಗುಣಮಟ್ಟದ ವಿಷಯಕ್ಕೆ ಬಂದಾಗ ಕನ್ನಡ ಮತ್ತು ತುಳು ವಿಕಿ ಮುಂಚೂಣಿಯಲ್ಲಿ ನಿಲ್ಲುತ್ತವೆ. ವಿಕಿ ಅಡ್ಮಿನ್ ನಿಮಗೆ ಸೂಚನೆ ಕೊಟ್ಟದ್ದರ ಉದ್ದೇಶ ಇದೇ ಆಗಿದೆ. ಧನ್ಯವಾದಗಳು [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::::ಮಹಾವೀರ ಇಂದ್ರರೇ ನಿಮ್ಮ ಸೂಚನೆ ಸರಿ, ಆದರೆ ಹೊಸ ಲೇಕನ ಬರಿಯುವ ಪಟ್ಟಿ ಹೆಚ್ಚಿಸಲು ಹೋಗಿ ಹಳೆ ಲೇಕನ ಕೈ ಬಿಟ್ಟರೆ ಹೇಗೇ? ಅದನ್ನು ಯಾರು ಬರಿಯುತ್ತಾರೆ. ವಿಷಯ ಇಲ್ಲದಿದ್ದರೆ ಸರಿ, ಇದ್ದಾಗ ಯಾಕೆ ಪೂರ್ತಿ ಮಾಡುವುದಿಲ್ಲ ಅಂತ ಆಶ್ಚರ್ಯ ಆಯಿತು. ನಾವು ತುಳುವರಿಗೆ ಬೇಕಾಗುವ ವಿಷಯ ತಿಳಿಸಬೇಕು. ಆರು ಏಳು ಸಾಲುಗಳು ಯಾಕೆ? ::::ನಿಮ್ಮ ಬರಹಗಳು ಉತ್ತಮವಾಗಿದೆ. ದನ್ಯವಾದಗಳು [[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) nfqrxxb6ajkh8fqa64e1tz91jlx0vy7 366176 366172 2026-08-21T10:05:11Z A826 4610 366176 wikitext text/x-wiki {{Template:Welcome|realName=|name=Shreelatha.Halemane}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೬:೫೫, ೫ ಮೇ ೨೦೨೪ (IST) == WP:MOS 2025 == ನಮಸ್ಕಾರ, ಇಂಚಿಪೊದ ಬರವುದ ಲೇಕನೊ [[ಗೋಬಿ ಮರುಭೂಮಿ]], [[ಬಹ್ರೇನ್ ರಾಷ್ಟ್ರೀಯ ಮ್ಯೂಸಿಯಂ]], [[ಕಂಬೋಡಿಯಾ]] [[ಹವರ್ ದ್ವೀಪೊಲು]], [[ತಲಾತ್ ರೋಟ್ಫೈ]] ಬೊಕ್ಕ ಮಸ್ತ್ ಲೇಕನೊಲು ಈ ರೀತಿ ರಚನೆ ಇಜ್ಜಿ. ವಿಕಿ ಕೈಪಿಡಿ ಶೈಲಿ ದಯಮಲ್ತ್ ತಿರ್ತುದ ಉಲ್ಲೇಕೊನು ಅನುಸರಿಸಲೆ [[:en:Wikipedia:Manual_of_Style/Layout|ಯೋಜನೆದ ಶೈಲಿದ ಕೆಪಿಡಿ]] [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೦:೫೭, ೬ ದಸಂಬರ್ ೨೦೨೫ (IST) == Invitation: Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026 == <div style="margin: 1em auto; padding: 15px; border: 2px solid #4b0082; border-radius: 15px; background-color: #fdfaf5; box-shadow: 5px 5px 15px rgba(0,0,0,0.1); max-width: 100%;"> <div style="text-align: center; margin-bottom: 20px;"> <span style="font-size: 160%; font-weight: bold; color: #cc5500; text-shadow: 1px 1px 2px #ddd;">✨ Karavali Wikimedians @ Mangaluru Lit Fest 2026 ✨</span><br> <span style="font-size: 120%; color: #4b0082; font-weight: bold;">Celebrating 10th Years of Tulu Wikipedia & 25th Years of Wikipedia</span></div> Dear Wikimedians, Warm greetings from '''Karavali Wikimedians User Group'''! 🌟 We are thrilled to invite you to the '''[[:m:Karavali Wikimedians Stall & Community Meetup at Mangaluru Lit Fest 2026|8th Edition of the Mangaluru Literature Festival (#MlrLitFest)]]'''. This year is a historic milestone as we kick-start the '''[[:m:Tulu Wikipedia 10th Anniversary|10th Anniversary of Tulu Wikipedia]]'''! <div style="display: flex; flex-wrap: wrap; gap: 10px; width: 100%; background: transparent;"> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #cc5500; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #cc5500;">📅 '''When:'''</span><br> '''10th & 11th January 2026''' <br><br> <span style="font-size: 110%; color: #cc5500;">📍 '''Where:'''</span><br> '''Dr. TMA Pai International Convention Centre, Mangaluru''' </div> <div style="flex: 1; min-width: 250px; padding: 10px; border: 1px dashed #2e8b57; border-radius: 10px; background: #ffffff; box-sizing: border-box;"> <span style="font-size: 110%; color: #2e8b57;">🚀 '''The Wiki Stall:'''</span><br> Visit our stall to see our work in language documentation and cultural preservation. We need your energy to help manage and showcase our projects! </div></div> <div style="margin-top: 20px; padding: 15px; background-color: #f3eafb; border-left: 10px solid #4b0082; border-radius: 5px;"> <span style="font-size: 130%; font-weight: bold; color: #202122;">📌 Special Community Meetup</span><br> '''Date:''' 11th January 2026 (Day 2)<br> '''Time:''' 11:00 AM (Followed by Lunch 🍛)<br> '''Agenda:''' '''''Future roadmaps, upcoming projects, Community health and capacity building.''''' </div> <br> * We warmly invite all community members to attend both days of the festival, and we strongly encourage you not to miss the second day’s meetup, which will be an important space for collective reflection and future planning. We look forward to your presence, participation, and support in making this celebration and gathering meaningful and successful. '''Warm regards,'''<br/> <span style="color: #4b0082; font-weight: bold;">President and Secretary</span><br/>Karavali Wikimedians User Group, Mangaluru</div> <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list/MlrLitFest&oldid=29873017 --> == Wikimedian's Group of Mainland China wishes you a Happy New Year! == <div class="nomobile"><div style="position:relative; width:600px; height:800px;"> {| style="background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:0px; width:100%; height:100%; position:absolute; top:0; left:0; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2); box-sizing:border-box;" | style="vertical-align:middle; padding:10px; position:relative; height:100%;" |<span style="position:absolute; top:50%;left:50%;transform:translate(-50%,-50%); opacity:0.1; pointer-events:none; z-index:0;">[[File:丙午新年标志.svg|400px|link=]]</span><span style="position:absolute; top:15px; left:25px; color:#7D1506; font-weight:bold; font-size:2.6em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span><br><div style="position:absolute; top:100px; left:25px; right:25px; color:#333333; font-weight:400; font-size:1.3em; line-height:1.5; z-index:1;">'''Dear Shreelatha.Halemane''':<br><br>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!<br><br><div style="text-align:right; margin-top:8px;">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div></div> |} </div></div>{{If mobile |tag=div |1=<div style="box-sizing:border-box; width:100%; max-width:560px; margin:0 auto; background-color:#F9EBDA; background-image:linear-gradient(131deg, transparent 64%, #E8D3BA 64%, #E8D3BA 66%, #F0DDC5 66%, #F0DDC5 100%); border:8px solid #7D1506; border-radius:8px; padding:14px; box-shadow:2px 2px 15px rgba(125, 21, 6, 0.2);"> <div style="text-align:center; margin-bottom:12px;"> <span style="color:#7D1506; font-family:'LiSu', 'STXingkai', 'KaiTi', serif; font-weight:bold; font-size:2.2em; text-shadow:1px 1px 2px rgba(0,0,0,0.1);">Happy Chinese New Year!</span> </div> <div style="display:flex; align-items:center; gap:12px;"> <div style="flex:1 1 auto; font-size:14px; line-height:1.8; color:#333333; "> <div><big>'''Dear Shreelatha.Halemane:'''</big></div><br> <div>As the Year of Snake glides into the shadow, <br>the noble Steed of Horse year gallops toward the dawn. <br><br>With a fluid quill, your masterful edits have woven the rich tapestry of the winter past; <br>Now, the heraldic horse steps forth, its joyful call echoing the vibrant promise of spring. <br><br>Thank you for {{fontcolor|#333333|link=ವಿಕಿಪೀಡಿಯ:ಸ್ಪ್ರಿಂಗ್_ಮಂತ್_ಎಡಿಟ್-ಅಥಾನ್|text=illuminating Wikipedia with such festive warmth}} at the turn of the year. <br><br>May this Chinese New Year bestow upon you enduring health and untroubled peace. <br>May you take the reins of your grandest ambitions, and may your endeavors enjoy unbridled success!</div> </div> <div style="flex:0 0 auto;"><span style="display:block; opacity:0.9;">[[File:丙午新年标志.svg|150px|link=]]</span></div> </div> <div style="text-align:right; margin-top:8px; font-size:14px">''Your sincerely, <br>{{fontcolor|#333333|link=m:WMGMC|text=Wikimedian's Group of Mainland China}}''</div> </div> </div> }} —[[ಬಳಕೆದಾರೆ:FradonStar|FradonStar]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:FradonStar|ಪಾತೆರ್ಲೆ]]) ೦೯:೦೫, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) == ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026ದ ಪುಗರ್ತೆ == <div lang="tcy" dir="ltr"> <div style="border: 2px solid maroon; background-color: #FAFAD2; color: #202122; padding: 1em; text-align: left;"> [[File:Feminism and Folklore 2026 logo-tcy.svg | 350px | right]] '''ನಮಸ್ಕಾರ {{ROOTPAGENAME}}''',<br/> [[ವಿಕಿಪೀಡಿಯ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026|ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026]] ಗ್ ಕೊಡುಗೆ ಕೊರ್ತಿನಕ್ಲೆಗ್ ಧನ್ಯವಾದೊಲು!<br/> ವಿಕಿಪೀಡಿಯೊಡು ಜಾನಪದೊಡು ಪೊಣ್ಣುಲೆನ ಬಗ್ಗೆ ಲೇಕನೊಲೆನ್ ಬರೆಯೆರೆ ನಿಕ್ಲೆನ ಅವಿಶ್ರಾಂತ ಪ್ರಯತ್ನೊಗು ಎಂಕುಲು ಮೆಚ್ಚುಗೆ ಕೊರೊಂದುಲ್ಲ. ವಿಕಿಪೀಡಿಯೊಡು ಲಿಂಗ ಬುಕ್ಕೊ ಸಾಂಸ್ಕೃತಿಕ ಅಂತರೊನು ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ನಿಕ್ಲೆನ ನಿರಂತರ ಪ್ರಯತ್ನೊಲು, ಸಮರ್ಪಣೆಡ್ದ್ ಎಂಕುಲು ಮಸ್ತ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯೊಂದುಲ್ಲ. ಈರ್ ಪ್ರತಿ ವರ್ಷಲಾ ಪಾಲ್ ಪಡೆಯೊಡು ಪಂದ್ ಎಂಕುಲು ಆಸೆ ಮಲ್ಪುವ! ಪ್ರಾಮಾಣಿಕವಾದ್,<br/> '''ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2026 - ಸ್ಥಳೀಯ ಸಂಘಟನೆ ತಂಡ''' </div></div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:%E0%B2%B8%E0%B3%8D%E0%B2%A4%E0%B3%8D%E0%B2%B0%E0%B3%80%E0%B2%B5%E0%B2%BE%E0%B2%A6%E0%B3%8A_%E0%B2%AC%E0%B3%81%E0%B2%95%E0%B3%8D%E0%B2%95%E0%B3%8A_%E0%B2%9C%E0%B2%BE%E0%B2%A8%E0%B2%AA%E0%B2%A6%E0%B3%8A_2026/%E0%B2%AA%E0%B2%BE%E0%B2%B2%E0%B3%8D_%E0%B2%A6%E0%B3%86%E0%B2%A4%E0%B3%8A%E0%B2%A8%E0%B3%81%E0%B2%A8%E0%B2%95%E0%B3%81%E0%B2%B2%E0%B3%81/list&oldid=357914 --> == Meeting Invitation: Karavali Wikimedians User Group (KWUG) == '''Namaskara!''' You are cordially invited to the upcoming online meeting of the Karavali Wikimedians User Group. We will be discussing key projects, including the 10th-anniversary celebrations for Tulu Wikipedia and our cultural heritage campaigns. '''Meeting Details:''' <br/> 📅 Date: Thursday, 23 April 2026 <br/> ⏰ Time: 07:30 PM (IST) <br/> 📍 Venue: Google Meet (Online) <br/> 🔗 Meeting Link: https://meet.google.com/xgs-fodf-ouy '''Agenda:''' <br/> Wiki Loves Tuluvas: Living and Cultural Heritage campaign. <br/> Tulu Wikipedia 10th Anniversary: Planning the milestone celebration. <br/> In-person Meeting: Scheduling our next physical gathering. <br/> GLAM Activity: Updates on gallery and library collaborations. <br/> Open Discussion: Other community matters. Your participation and ideas are vital to the growth of our community. See you there... '''More:''' <br/> https://meta.wikimedia.org/wiki/Karavali_Wikimedians/Meetups/Online_Meetup_23_April_2026 '''On behalf''', <br/> Karavali Wikimedians <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Karavali_Wikimedians/Events/delivery_list&oldid=30440036 --> == ತುಳು ವಿಕಿಪೀಡಿಯ ೧೦ನೇ ವರ್ಸಂತಿಗೆದ ಬರೆವು ಪಂತೊಗು ಲೆಪ್ಪೋಲೆ! == {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | '''ನಮಸ್ಕಾರ!''' 🙏 ನಮ್ಮ ತುಳು ವಿಕಿಪೀಡಿಯ ಸುರು ಆದ್ ಇತ್ತೆ ಪತ್ತ್ ವರ್ಷದ ಸಂಭ್ರಮೋಡುಂಡು. ಈ ಎಡ್ಡೆ ಲೇಸ್‌ದ ನೆನಪುಗಾದ್, ನಮ್ಮ ತುಳು ಬಾಸೆ ಬುಕ್ಕೊ ಸಂಸ್ಕೃತಿನ್ ಇಂಟರ್ನೆಟ್‌ಡ್ ಜಗತ್ತ್‌ಗೇ ತೆರಿಪಾಯೆರೆ ಒಂಜಿ ಪೊರ್ಲುದ '''ಲೇಖನ ಬರೆಪುನ ಪಂತೊನು''' ದೀಡುಂಡು. ನಮ್ಮ ತುಳುನಾಡ್‌ದ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ, ಆಚಾರ-ವಿಚಾರದ ಬಗ್ಗೆ ಜ್ಞಾನ ಉಪ್ಪುನ ಮಾತೆರೆಗ್‌ಲಾ ಮುಲ್ಪ ಮುಕ್ತ ಆಯಿನ ಅವಕಾಶ ಉಂಡು! === 📅 ಪಂತೊದ ದಿನೊಕ್ಕುಲು === * '''ಸುರು ಆಪುನ ದಿನ:''' ಜೂನ್ ೫, ೨೦೨೬ (00:00 UTC) * '''ಮುಗಿಯುನಾ ದಿನ:''' ಆಗಸ್ಟ್ ೪, ೨೦೨೬ (11:59 UTC) === 📝 ಪಂತೊದ ನಿಯಮೊಲು === # '''ವಿಷಯ:''' ನಮ್ಮ ಸ್ಥಳೀಯ ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ತುಳುನಾಡ್‌ದ ಬಗ್ಗೆ ಇಪ್ಪೊಡು. # '''ಗಾತ್ರ:''' ಪೊಸ ಲೇಖನ ಅತ್ತಂಡ ಎಕ್ಸ್‌ಪ್ಯಾಂಡ್ ಮಲ್ತಿನ ಲೇಖನ ಕಮ್ಮಿ ಪಂಡಲಾ '''೩೦೦ ಪದೊಕುಲು''' (ಅತ್ತಂಡ ೩,೦೦೦ ಬೈಟ್ಸ್) ಇಪ್ಪೊಡು. # '''ಗುಣಮಟ್ಟ:''' ಸ್ವಂತ ಬರೆತಿನ ಇಪ್ಪೊಡು (Copy-paste ಬುಕ್ಕೊ ಮೆಷಿನ್ ಟ್ರಾನ್ಸ್‌ಲೇಟ್ ಮಲ್ದಿನ ಲೇಖನೊಲು ಬಲ್ಲಿ). ಸರಿಯಾಯಿನ ಆಧಾರ/ಲಿಂಕ್ ದೀಡೊಡು. === 🏆 ಬೊಮ್ಮನೊಲು === * ವಿಶೇಷ ಬಹುಮಾನೊಲು ಬುಕ್ಕೊ ಸರ್ಟಿಫಿಕೇಟ್‌ಲು ಉಂಡು! (ವಿವರೊಲು ಬೇಗನೇ ಪ್ರಕಟ ಆಪುಂಡು). ---- 📢 '''ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಬುಕ್ಕೊ ಪಾಲ್ ಪಡೆಯರೆ ಈ ಪುಟೊಗು ಬಲೆ:''' [[tcy:ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ|ವಿಕಿಪೀಡಿಯ:10ನೇ ವರ್ಸಂತಿಗೆದ ಬರವು ಪಂತೊ]] ನಿಕ್ಲೆನ ಅಕ್ಷರೊಲು ನಮ್ಮ ತುಳು ಬಾಸೆಗ್ ಬಲ ಕೊರಡ್. ಬಲೆ, ಮಾತೆರ್ಲಾ ಒಟ್ಟಿಗೆ ಸೇರ್ದ್ ತುಳು ವಿಕಿಪೀಡಿಯನ್ ಬಂಗಾರ ಮಲ್ಪುಗ! |} [[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೧೯:೪೮, ೪ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:ChiK@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Tulu_Wikipedia_10th_Anniversary/delivery_list&oldid=30611683 --> == Wikipedia Asian Month 2025 Golden Barnstar == <div lang="en" dir="ltr"> <div style="text-align: center;"> [[File:2025 Wikipedia Asian Month Special Barnstar.png|500px]]</div> [[User:Betty2407|Betty2407]] ([[User talk:Betty2407|talk]]) 08:38, 6 June 2026 (UTC) on behalf of Wikipedia Asian Month User Group </div> <!-- Message sent by User:Betty2407@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Betty2407/WAMMassMessagelist2&oldid=30642952 --> == Congratulations to the Local Winners of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, The Feminism and Folklore International Organizing Team is pleased to congratulate the Local Winners of '''[[:m:Feminism and Folklore 2026|Feminism and Folklore 2026]]'''. Local Winners are participants who achieved top rankings within their respective language communities through their valuable contributions to documenting folklore, women's history, culture, traditions, and related knowledge on Wikimedia projects in their local languages. Your efforts have helped enrich Wikimedia content, preserve cultural heritage, and improve the representation of diverse communities and knowledge systems across the movement. We sincerely appreciate your dedication and contribution to the success of the campaign. === Appreciation Form === As a token of appreciation to receive prizes, all Local Winners are requested to complete the following form by '''22 June 2026'''. '''Form link''' - [https://docs.google.com/forms/d/e/1FAIpQLSdGU7vkzANkB8P1k2X-NPwOwqBoTZafzgUZ-23wnVCeW2cj3g/viewform here] ; Deadline : 22 June 2026 Please ensure that all information provided in the form is accurate and complete. This information will be used for the appreciation process. === Thank you === We extend our heartfelt thanks to all Local Winners for their outstanding contributions and continued support of free knowledge. Your work has strengthened local-language content and helped make knowledge about folklore and women more accessible to communities around the world. Congratulations once again on your achievement. —''Feminism and Folklore International Organizing Team'' </div> --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೨:೫೫, ೭ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30649454 --> == Thank you for being a part of Feminism and Folklore 2026 == <div lang="en" dir="ltr" class="mw-content-ltr"> [[File:Feminism and Folklore 2026 logo.svg|right|350px]] Dear {{PAGENAME}}, Your contributions helped make this year's [[:Feminism and Folklore 2026|Feminism and Folklore]] campaign a great success, and we are truly grateful for your time, knowledge, and dedication to documenting women's stories and folklore on Wikimedia projects. As we reflect on this year's campaign and prepare for the next edition, we'd love to hear about your experience. Please take a few minutes to fill out our feedback form and share your thoughts and suggestions. As a token of our appreciation, everyone who completes the form will receive a '''special digital postcard'''! in their email. This year marks '''''25 years of Wikipedia''''', and the postcard has been created to celebrate this milestone and to recognize the incredible efforts of participants like you who continue to enrich free knowledge for the world. '''Form Link - [https://docs.google.com/forms/d/e/1FAIpQLSdqFZN9IdWbAnZ_7DyG5bIsuq7KAkdsIxweC_TdPH9In-tthQ/viewform?usp=dialog here]''' Please complete the feedback form by '''25 June 2026'''. Thank you once again for making Feminism and Folklore 2026 a success. We hope to see you again in future campaigns! Best regards, Feminism and Folklore international Team. --[[ಬಳಕೆದಾರೆ:MediaWiki message delivery|MediaWiki message delivery]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:MediaWiki message delivery|ಪಾತೆರ್ಲೆ]]) ೨೦:೦೧, ೧೮ ಜೂನ್ ೨೦೨೬ (IST) </div> <!-- Message sent by User:Tiven2240@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=User:Tiven2240/wlf2026&oldid=30704478 --> == ತುಳು ವಿಕಿಪೀಡಿಯಾದ ೧೦ನೇ ವರ್ಸೊದ ಸಂಭ್ರಮದ ಉಚ್ಚಯ == <div style="background: #fffdf5; border: 2px solid #800000; border-radius: 8px; padding: 18px; max-width: 650px; margin: 15px auto; font-family: sans-serif; color: #222; line-height: 1.6;"> <!-- Banner Header --> <div style="text-align: center; border-bottom: 2px solid #ffe082; padding-bottom: 10px; margin-bottom: 15px;"> <h2 style="color: #d84315; margin: 0 0 5px 0; border: none; font-size: 22px;">🎉 ತುಳು ವಿಕಿಪೀಡಿಯದ ೧೦ನೇ ವರ್ಷದ ಆಚರಣೆ</h2> <p style="margin: 0; font-weight: bold; color: #555;">ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪ್ಪುಲು!</p></div> <p>ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13ನೇ ತಾರೀಕ್ದಾನಿ ಕುಡ್ಲಡ್ ನಡಪರೆ ಇಪ್ಪುನ ತುಳು ವಿಕಿಪೀಡಿಯದ ಪತ್ತನೇ ವರ್ಷದ ಆಚರಣೆನ್ ತೆರಿಪಾವರೆ ಎಂಕ್ಲೆಗ್ ಮಸ್ತ್ ಕುಸಿ ಆವೊಂದುಂಡು.</p> <!-- Key Event Details --> <div style="background: #fff8e1; border-left: 4px solid #ff8f00; padding: 10px 14px; margin: 12px 0;"><b>📅 ತಾರೀಕ್:</b> ಸೆಪ್ಟೆಂಬರ್ 12 ಬುಕ್ಕೊ 13<br><b>📍 ಜಾಗೆ:</b> ಕುಡ್ಲ (ಜಾಗೆ ನಿಗಾಂಟಾತ್ಜಿ)</div> <p>ಆಚರಣೆ ಮಲ್ಪಿಯರ ಸುರು ಆತ್ಂಡ್. ಜವಾಬ್ದಾರಿಲೆನ್ ತಂಡೊ ಆದ್ ಪಟ್ಟೊಂದು, ಒಟ್ಟು ಸೇರ್ದ್ ಈ ಮೈಲಿಗಲ್ಲ್ನ್ ಮಾತೆರೆಗ್ಲಾ ಒಂಜಿ ನೆಂಪು ಇಪ್ಪುನಂನೆ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎಂಕುಲು ಎದುರು ತೂವೊಂದುಲ್ಲ.</p> <p>ಜವಾಬ್ದಾರಿ ದೆತೊಂದು ಸ್ವಯಂಸೇವಕೆರಾದ್ ಈ ಲೇಸ್ಗ್ ಸಕಾಯೊ ಕೊರಿಯರ ಇಚ್ಚೆ ಇತ್ತ್ಂಡ, ದಯಮಲ್ತ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ. ಪಾಲ್ ದೆತೊನಿಯರ ಮಾತೆರೆಗ್ಲಾ ಅವಕಾಸೊ ಉಂಡು ಅಂಚನೆ ನಿಕ್ಲೆನ ಬೆರಸಾಯೊ ಇತ್ತ್ಂಡ ಎಂಕ್ಲೆಗ್ ಕುಸಿಯಾಪುಂಡು.</p> <p>ವ್ಯವಸ್ತೆ ಮಲ್ಲಪುನೆ - ವಿಸೇಸವಾದ್ ಒನಸ್ ತೆನಸ್ ಬುಕ್ಕೊ ಲಾಜಿಸ್ಟಿಕ್ಸ್ ಆಯೋಜಿಸಯರ ದಯಮಲ್ತ್ ಲಗತ್ತಿಸಾಯಿನ ಗೂಗಲ್ ಫಾರ್ಮ್ನ್ ಬರ್ತಿ ಮಲ್ತ್ದ್ ನಿಗ್ಲ್ ಬರ್ಪುನೆನ್ ಎಂಕ್ಲೆಗ್ ತೆರಿಪಾಲೆ.</p> <!-- CTA Registration Button --> <div style="text-align: center; margin: 20px 0; background: #fff8e1; color: #ffffff !important; padding: 10px 20px; text-decoration: none; font-weight: bold; border-radius: 4px; display: inline-block;">[https://docs.google.com/forms/d/e/1FAIpQLSenIOP2YtOn3bbxSU2ijO5YeYveIECUu_UovVREKz7Iy-jkjg/viewform?usp=dialog📝 ಗೂಗಲ್ ಫಾರ್ಮ್ ಬರ್ತಿ ಮಲ್ಪಿ (ಆಗಸ್ಟ್ 13 ಮುಟ್ಟ)]</div> <!-- Note Box --> <div style="background: #e8f5e9; border: 1px solid #c8e6c9; border-radius: 4px; padding: 10px 14px; font-size: 14px; color: #1b5e20; margin: 12px 0;"><b>📌 ಗಮನಿಸಲೆ:</b> ನಮೊ ಒಂಜಿ ಕಿನ್ಯ ಬಜೆಟ್ಡ್ ಬೇಲೆ ಮಲ್ತೊಂದಿಪ್ಪುನೆ. ಅಯಿಡ್ದಾದ್ ನೆರೆಕರೆ ತಾಲೂಕು, ಜಿಲ್ಲೆದಗ್ಲ್ ಪಾಲ್ ಪಡೆವೊನ್ಬೆರ್. ಅಂಚನೇ ದೂರೊದ ಜಿಲ್ಲೆದಕ್ಲ್ ಬರೊಂದುಲ್ಲೆರ್. ಕುಡ್ಲದ ಪಿದಯಿಡ್ದ್ ಬರ್ಪುನಕ್ಲೆಗ್ ಉಂತುನೆಗ್ ಬೆಂಬಲೊ ಕೊರೊಂದುಲ್ಲೊ. ದೂರೊದಕ್ಲೆಗ್ ಉಂತುನ ವ್ಯವಸ್ತೆನ್ ಮಲ್ಪೊಡು, ಅಂಚನೆ ವನಸ್ಗ್ ಪನೊಡು ಪನ್ಪುನ ನೆಲೆಟ್ ನಿಕ್ಲ್ ಬರ್ಪುನೆನ್ ದುಂಬೆ ಪನ್ಲೆಂದ್ ವಿನಂತಿ ಮಲ್ತೊಂದುಲ್ಲೊ.</div> <p>ಕುಡ್ಲದ ಮಾತೆರೆನ್ಲಾ ತುಳು ವಿಕಿಪೀಡಿಯದ 10 ವರ್ಸೊದ ಗೌಜಿನ್ ಒಟ್ಟಾದ್ ಆಚರಣೆ ಮಲ್ಪಿಯರ ಎದುರು ತೂವೊಂದುಲ್ಲೊ! 🙌</p> <!-- Signature --> <div style="text-align: right; margin-top: 15px; font-size: 14px; color: #666; border-top: 1px solid #eee; padding-top: 8px;"> ಪತ್ತನೇ ವರ್ಸೊದ ತಯಾರಿಡ್ ಪಾಲ್ ಪಡೆಯಿನ ಮಾತೆರೆಗ್ಲಾ ಎಡ್ಡೆಪುದ ಸೊಲ್ಮೆಲು.<br> <b>ಈವೆಂಟ್ ಕೋ-ಆರ್ಡಿನೇಟರ್</b>, ತುಳು ವಿಕಿಪೀಡಿಯ ಪತ್ತನೆ ವರ್ಸಾಚರಣೆ</div> </div> <!-- Message sent by User:ChiK@tcywiki using the list at https://tcy.wikipedia.org/w/index.php?title=%E0%B2%B5%E0%B2%BF%E0%B2%95%E0%B2%BF%E0%B2%AA%E0%B3%80%E0%B2%A1%E0%B2%BF%E0%B2%AF:10%E0%B2%A8%E0%B3%87_%E0%B2%B5%E0%B2%B0%E0%B3%8D%E0%B2%B8%E0%B2%82%E0%B2%A4%E0%B2%BF%E0%B2%97%E0%B3%86/%E0%B2%B2%E0%B3%86%E0%B2%AA%E0%B3%8D%E0%B2%AA%E0%B3%8B%E0%B2%B2%E0%B3%86&oldid=365205 --> == Your article [[ಮನಂಚಿರ]] == [[File:Information.svg|25px|alt=Information icon]] Welcome, and thank you for [[Special:Contributions/Shreelatha.Halemane|contributing]] the page [[:ಮನಂಚಿರ]] to Wikipedia. While you have added the page to the ''Tulu'' version of Wikipedia, the article is not in Tulu; if you would like to contribute to a Wikipedia in another language, please visit the [[list of Wikipedias]] and see if a version of Wikipedia exists in your desired language. Otherwise, we invite you to translate it into Tulu. It has been listed at [[WP:PNT|Pages needing translation into Tulu]], but if it is not translated within two weeks, the article may be listed for deletion. Thank you. <!-- Template:uw-nottulu --> [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೨೩:೪೭, ೧೭ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]] moved to draftspace == Thanks for your contributions to [[Draft:ಮನಂಚಿರ|ಮನಂಚಿರ]]. Unfortunately, I do not think it is ready for publishing as a live article at this time because '''it is a poor translation''' and '''it consists of machine-generated text'''. I have converted it to a draft which you can improve, undisturbed for a while. Please see more information at [[:en:Help:Unreviewed new page|Help:Unreviewed new page]]. When the article is ready for publication, please click on the "Submit the draft for review!" button at the top of the page OR move the page back. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೭:೧೭, ೧೮ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == August 2026 == [[File:Stop hand nuvola.svg|30px|alt=Stop icon]] You may be '''[[:en:Wikipedia:Blocking policy|blocked from editing]] without further warning''' the next time you edit Wikipedia using a [[:en:large language model]], which is [[:en:Wikipedia:Writing articles with large language models|prohibited]], as you did at [[:ಧೋಲಾವಿರ]]. [[ಬಳಕೆದಾರೆ:ChiK|ChiK]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ChiK|ಪಾತೆರ್ಲೆ]]) ೧೫:೫೫, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) === ತಪ್ಪು ಸರಿಪಡಿಸುವಿಕೆ === ಸರ್ ನಮಸ್ತೆ, ನಾನು ಬರೆದಿರುವ ಅರ್ಟಿಕಲ್ನಲ್ಲಿ ಎನಾದರೂ ತಪ್ಪುಗಳಿದ್ದಲ್ಲಿ ದಯವಿಟ್ಟು ತಿಳಿಸಿ. ಸರಿಯಾಗಿ ಯಾವ ಆರ್ಟಿಕಲ್ ಮತ್ತು ಎಲ್ಲಿ ಯಾವ ವಾಕ್ಯ ಸರಿಪಡಿಸಬೇಕೆಂಬುದನ್ನು ಸ್ಪಷ್ಟವಾಗಿ ತಿಳಿಸಿ.ಖಂಡಿತವಾಗಿಯೂ ಸರಿಪಡಿಸುತ್ತೇನೆ...ನನ್ನ ಮಾತೃ ಭಾಷೆ ಕನ್ನಡ ಆದ್ದರಿಂದ ತುಳುವಿನಲ್ಲಿ ಗೊತ್ತಿರುವ ರೀತಿಯಲ್ಲಿ ಅರ್ಟಿಕಲ್ ಮಾಡುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೪:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :ನಮಸ್ಕಾರ. :ಲೇಖನವನ್ನು ಯಾವುದೇ ಭಾಷೆಯಿಂದ ತುಳುವಿಗೆ ಅಥವಾ ಕನ್ನಡಕ್ಕೆ ಅನುವಾದಿಸುವಾಗ ಓದುಗರಿಗೆ ಅರ್ಥವಾಗಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ನಿಮ್ಮ ಧೋಲಾವಿರ ಬರಹದಲ್ಲಿ ಇರುವುದು ಇದೇ ಸಮಸ್ಯೆ- ನನ್ನ ಪ್ರತಿಕ್ರಿಯೆ ಈ ಲೇಖನಕ್ಕೆ ಮಾತ್ರ, ಬೇರೆ ಲೇಖನಗಳನ್ನು ನನು ಗಮನಿಸಿಲ್ಲ- ಬರೆದ ಲೇಖನವು ಅರ್ಥಬದ್ಧವಾಗಿ ಇರಬೇಕಾದ್ದು ಬಹಳ ಮುಖ್ಯ. ಸರಳವಾಗಿ ಎಲ್ಲರಿಗೂ ಅರ್ಥವಾಗುವ ಹಾಗೆ ಬರೆಯಿರಿ ಎಂಬ ವಿನಂತಿ. [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::ಸರಿಪಡಿಸಿ ಬರೆಯುತ್ತಿದ್ದೇನೆ. [[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೧, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) :::ಸರಿ. ಬೇರೆ ವಿಕಿಗೆ ಹೋಲಿಸಿದಾಗ ತುಳು ಮತ್ತು ಕನ್ನಡ ವಿಕಿಯಲ್ಲಿ ಬರಹಗಳ ಸಂಖ್ಯೆ ಕಡಿಮೆ ಇದೆ. ಆದರೆ ಅದೇ ಗುಣಮಟ್ಟದ ವಿಷಯಕ್ಕೆ ಬಂದಾಗ ಕನ್ನಡ ಮತ್ತು ತುಳು ವಿಕಿ ಮುಂಚೂಣಿಯಲ್ಲಿ ನಿಲ್ಲುತ್ತವೆ. ವಿಕಿ ಅಡ್ಮಿನ್ ನಿಮಗೆ ಸೂಚನೆ ಕೊಟ್ಟದ್ದರ ಉದ್ದೇಶ ಇದೇ ಆಗಿದೆ. ಧನ್ಯವಾದಗಳು [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೧೫:೦೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) ::::ಮಹಾವೀರ ಇಂದ್ರರೇ ನಿಮ್ಮ ಸೂಚನೆ ಸರಿ, ಆದರೆ ಹೊಸ ಲೇಕನ ಬರಿಯುವ ಪಟ್ಟಿ ಹೆಚ್ಚಿಸಲು ಹೋಗಿ ಹಳೆ ಲೇಕನ ಕೈ ಬಿಟ್ಟರೆ ಹೇಗೇ? ಅದನ್ನು ಯಾರು ಬರಿಯುತ್ತಾರೆ. ವಿಷಯ ಇಲ್ಲದಿದ್ದರೆ ಸರಿ, ಇದ್ದಾಗ ಯಾಕೆ ಪೂರ್ತಿ ಮಾಡುವುದಿಲ್ಲ ಅಂತ ಆಶ್ಚರ್ಯ ಆಯಿತು. ನಾವು ತುಳುವರಿಗೆ ಬೇಕಾಗುವ ವಿಷಯ ತಿಳಿಸಬೇಕು. ಆರು ಏಳು ಸಾಲುಗಳು ಯಾಕೆ? ::::ನಿಮ್ಮ ಬರಹಗಳು ಉತ್ತಮವಾಗಿದೆ. ದನ್ಯವಾದಗಳು [[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) tsd5p5ipa9ow9nhxdp78li1gr6w19xn ಜಲೇಹ್ ಎಸ್ಫಹಾನಿ 0 18644 366196 281197 2026-08-21T10:21:59Z ChiK 1136 366196 wikitext text/x-wiki {{Infobox writer | embed = | honorific_prefix = | name = ಜಲೇಹ್ ಎಸ್ಫಹಾನಿ | honorific_suffix = | image = Jaleh Esfahani - 1937 (Sepia).jpg | alt = <!-- descriptive text for use by speech synthesis (text-to-speech) software --> | caption = ಜಲೇಹ್ ಎಸ್ಫಹಾನಿ 1937 ರಲ್ಲಿ | native_name = ژاله اسفاهاني | native_name_lang = | pseudonym = | birth_name = | | birth_place = ಇಸ್ಫಹಾನ್, ಇರಾನ್ | death_date = 29 ನವೆಂಬರ್ 2007 | death_place = [[ಲಂಡನ್]], ಇಂಗ್ಲೆಂಡ್ | resting_place = | occupation = | language = | alma_mater = ಬಾಕು ರಾಜ್ಯ ವಿಶ್ವವಿದ್ಯಾಲಯೊ <br/> ಮಾಸ್ಕೋ ಸ್ಟೇಟ್ ಯೂನಿವರ್ಸಿಟಿ|ಲೊಮೊನೊಸೊವ್ ಮಾಸ್ಕೋ ವಿಶ್ವವಿದ್ಯಾಲಯೊ | period = | notable_works = | spouse = {{marriage|ಶಮ್ಸ್-ಅಲ್-ದಿನ್ ಬಡಿ' ತಬ್ರಿಜಿ|1946}} | partner = | children = 2 }} '''ಜಲೇಹ್ ಎಸ್ಫಹಾನಿ''' (1921 - 29 ನವೆಂಬರ್ 2007) ಇರಾನ್‌ದ ಕವಿ ಬುಕ್ಕೊ ಸಾಹಿತ್ಯ ವಿದ್ವಾಂಸೆರ್. == ಸುರುತ ಜೀವನ == '''ಜಲೆಹ್ ಎಸ್ಫಾಹಾನಿ''' ('''ಇಥಾಲ್''' ಪಂಡ್ದ್ ನೋಂದಾಯಿತ ಆತ್ಂಡ್) 1921ಡ್ ಇಸ್ಫಾಹಾನ್‌ಡ್ ಒಂಜಿ ಜಮೀನ್ದಾರೆರೆನ ಕುಟುಂಬೊಡು ಪುಟ್ಟುವೆರ್. ಆ ಕಾಲೊಡು ಬ್ರಿಟಿಷ್ ಮಿಷನರಿಲು ನಡಪಾವೊಂದಿತ್ತಿನ ಬೆಹೆಸ್ಟ್ ಐನ್ ಶಾಲೆಡ್ ಬುಕ್ಕೊ '''ತೆಹ್ರಾನ್''' ಡ್ ನೂರ್ಬಾಸ್ ಪ್ರೌಢಶಾಲೆಡ್ ಶಿಕ್ಷಣ ಪಡೆಯೆರ್. ಅರೆನ ಅಪ್ಪೆ ಅರೆನ ಶಾಲೆಗ್ ಪೋಯೆರೆ ಅನುಮೋದನೆ ಇಜ್ಜಂದಿನ ನಡುಟು, ಎಸ್ಫಹಾನಿ ಅರೆನ ಅಮ್ಮೆರೆಗ್ ಅಲೆನ ಶಿಕ್ಷಣೊಡು ಪ್ರಮುಖ ಪಾತ್ರ ವಹಿಸೊಂದಿತ್ತಿನ ಶ್ರೇಯೊನು ಕೊರ್ಪೆರ್. ಎಸ್ಫಹಾನಿ ಏಳ್ ವರ್ಸೊದ ಪೊಣ್ಣು ಆನಗ ಸುರುಕು ಕಬಿತೆಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಸುರು ಮಲ್ತೆರ್. ಪ್ರೌಢಶಾಲೆಡ್ ಕಲ್ಪುನ ಪೊರ್ತುಡು "ಅಹ್ತರ್", "ಸೇಪಂತ" ಬುಕ್ಕೊ "ಬದ್ತಾರ್" ಇಂಚಿತ್ತಿನ ಪತ್ರಿಕೆಲೆಡ್ ಆರೆನ ಕಬಿತೆಲು ಪ್ರಕಟ ಆವೊಂದಿತ್ತ್ಂಡ್. ಎಸ್ಫಹಾನಿನ ಸುರುತ ಕಬಿತೆ ಸಂಕಲನ "ಗೊಲ್ಹಾ-ಯೆ ൵odru" (ಕಾಡು ಪುಷ್ಪೊಲು) 1945ಡ್ ಟೆಹ್ರಾನ್‌ಡ್ ಪ್ರಕಟ ಆಂಡ್.<ref name="EI">{{cite encyclopedia |title=ESFAHANI, Jaleh|encyclopedia=Encyclopedia Iranica|date=20 July 2009|last=Vajdi|first=Shadab|url=https://www.iranicaonline.org/articles/esfahani-jaleh/}}</ref> ಶಾಲೆಯಿಂದ ಪದವಿ ಪಡೆದ ನಂತರ, ಅವರು ನ್ಯಾಷನಲ್ ಬ್ಯಾಂಕ್‌ನಲ್ಲಿ ಕೆಲಸ ಮಾಡಿದರು, ಅಲ್ಲಿ ಅಕೌಂಟೆಂಟ್ ಆಗಿ ನೇಮಕಗೊಂಡ ಕೇವಲ ಮೂವರು ಮಹಿಳೆಯರಲ್ಲಿ ಒಬ್ಬರು.<ref>{{cite encyclopedia|title=Shahnāz A‘lāmī|encyclopedia=Women Poets Iranica |year=2022|last=Yousef|first=Saeed|url=https://poets.iranicaonline.org/article/shahnaz-alami-1921-2003-iranian-educator-activist-for-womens-rights-poet-and-writer/}}</ref> == ವೃತ್ತಿ == ಇರಾನ್ ದ ಬರವುದಕುಲೆನ ಸುರುತ ಸಮ್ಮೇಳನೊನು 1946ಡ್ ಟೆಹ್ರಾನ್ ಡ್ ನಡಪಾಯೆರ್, ಬುಕ್ಕೊ ಎಸ್ಫಾಹಾನಿ ಒಂಜೇ ಒರ್ತಿ ಪೊಣ್ಣು ಹಾಜರಾತಿನಕುಲು, ಬುಕ್ಕೊ 2000 ಜನೊಕ್ಲೆಗ್ ಒಂಜಿ ಕಬಿತೆನ್ ಪಠಣ ಮಲ್ತೆರ್.<ref name="Times">{{cite news|title=Jaleh Esfahani|url=https://www.thetimes.com/article/jaleh-esfahani-lp0ndxmgtk3|work=The Times|date=18 December 2007}} {{subscription required}}</ref> ಅವ್ವೇ ವರ್ಷ, ಇರಾನ್ ದ '''ಕಮ್ಯುನಿಸ್ಟ್ ಟುಡೇ''' ಪಾರ್ಟಿದ ಸದಸ್ಯೆ ಬುಕ್ಕೊ ಪಹ್ಲವಿ ಆಡಳಿತದ ವಿರೋಧಿ '''ಶಮ್ಸ್-ಅಲ್-ದಿನ್ ಬದಿ ತಬ್ರಿಜಿನ್''' ಮದಿಮೆ ಆಯೆರ್. ಶಾ ಮೊಹಮ್ಮದ್ ರೆಜಾ ಪಹ್ಲವಿನ ಮಿತ್ತ್ ವಿಫಲವಾದ್ ಕೊಲೆ ಮಲ್ಪುನ ಪ್ರಯತ್ನೊದ ಬುಕ್ಕೊ ತುಡೆಹ್ ಪಕ್ಷೊನು ನಿಷೇಧ ಮಲ್ತಿನಗ, ಜೋಡಿ ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟೊಗು ಪಲಾಯನ ಮಲ್ತೆರ್, [[ಬಾಕು]] ಡ್ ನೆಲೆಯಾಯೆರ್. ಅಲ್ಪ ಉಪ್ಪುನಗ, ಎಸ್ಫಹಾನಿ ರಡ್ಡ್ ಜೋಕುಲೆಗ್ ಜನ್ಮ ಕೊರಿಯೆರ್. 1952ಡ್ ಬಾಕು ರಾಜ್ಯ ವಿಶ್ವವಿದ್ಯಾಲಯದ ಭಾಷಾಶಾಸ್ತ್ರ ವಿಭಾಗೊಡ್ದ್ ಪದವಿ ಪಡೆಯೆರ್. ೧೯೫೪ಡ್, ಕುಟುಂಬ [[ಮಾಸ್ಕೋ]] ಗ್ ಪೋಯೆರ್. ಆರ್ [[ಮಾಸ್ಕೋ ಸ್ಟೇಟ್ ಯೂನಿವರ್ಸಿಟಿ|ಲೊಮೊನೊಸೊವ್ ಮಾಸ್ಕೋ ಯೂನಿವರ್ಸಿಟಿ]] ಡ್ "ಇರಾನ್ ಡ್ ಆಧುನಿಕ ಕವಿತೆ" ಪನ್ಪಿನ ಶೀರ್ಷಿಕೆಡ್ ಅರೆನ[[ಪ್ರಬಂಧ|ಡಾಕ್ಟರೇಟ್ ಪ್ರಬಂಧ]] ನ್ ಯಶಸ್ವಿಯಾದ್ ಮಂಡಣೆ ಮಲ್ತೆರ್. ೧೯೬೦ ಡ್ದ್ ೧೯೮೦ ಮುಟ್ಟ, ಎಸ್ಫಹಾನಿ ಒರಿ ಸಾಹಿತ್ಯ ವಿದ್ವಾಂಸೆರಾದ್ ಬೇಲೆ ಮಲ್ತೆರ್ ಬುಕ್ಕೊ [[ಮ್ಯಾಕ್ಸಿಮ್ ಗೊರ್ಕಿ ಸಾಹಿತ್ಯ ಸಂಸ್ಥೆ]] ಡ್ ಪರ್ಷಿಯನ್ ಬಾಸೆನ್ ಕಲ್ಪಾವೆರ್. ಅಲ್ಪ ಉಪ್ಪುನಗ, ಆರ್ ಕನಿಷ್ಠ ನಾಲ್ ಸಂಶೋಧನಾ ಯೋಜನೆಲೆನ್ ವಿವಿಧ ಕವಿಲೆನ್ ಬುಕ್ಕೊ ಆಧುನಿಕ ಪರ್ಷಿಯನ್ ಕಾವ್ಯೊದ ವಿವಿಧ ಅಂಶೊಲೆನ್ ಮುಗಿತೆರ್, ಅವುಲು ಮುಖ್ಯವಾದ್ ಇರಾನ್, ಅಫ್ಘಾನಿಸ್ತಾನ ಬುಕ್ಕೊ ತಾಜಿಕಿಸ್ತಾನೊಡು ಆಧುನಿಕವಾದಿ ಕಾವ್ಯೊದ ವಿಕಾಸೊದ ಅಧ್ಯಯನಲಾ ಆಂಡ್.<ref name="EI" /><ref name="Guardian">{{cite news |last1=Karimi-Hakkak |first1=Ahmad |author1-link=Ahmad Karimi-Hakkak |title=Jaleh Mohajer-Esfahani |url=https://www.theguardian.com/books/2008/jan/08/culture.poetry |work=The Guardian |date=8 January 2008}}</ref> ರಷ್ಯಾಡ್ ಇತ್ತಿನ ಪೊರ್ತುಡು, ಪರ್ಷಿಯನ್ ಸಾಹಿತ್ಯೊದ ಒಂಜೇ ಒಂಜಿ ಪೊಣ್ಣು ಮೋನೆ ಪಂಡ್ದ್ ಪುದರ್ ಪಡೆಯೆರ್. ಪರ್ಷಿಯನ್ ಬಾಸೆ ಪಾತೆರುನಕುಲೆನ ನಡುಟು ಸಾಹಿತ್ಯಿಕ ಸಹಕಾರದ ಬಗ್ಗೆ ಪಾತೆರೊಲೆನ್ ಉತ್ತೇಜಿಸೆರೆ ಬುಕ್ಕೊ ಪ್ರಸ್ತುತ ಮಲ್ಪೆರೆ ಎಸ್ಫಾಹಾನಿ ತಾಜಿಕಿಸ್ತಾನ್, ಅಫ್ಘಾನಿಸ್ತಾನ ಬುಕ್ಕೊ ಮಧ್ಯ ಏಷ್ಯಾದ ಬೇತೆ ಭಾಗೊಲೆಗ್ ಆಗಾಗ್ಗೆ ಪೋಯೆರ್.<ref name="Times" /> ತಾಜಿಕಿಸ್ತಾನ್ ಡ್, ಎಸ್ಫಾಹಾನಿ ರಷ್ಯನ್ ಬುಕ್ಕೊ ಅಜೆರಿ ಟರ್ಕಿಶ್ ಬಾಸೆಡ್ ಬರೆತಿನ ಕಬಿತೆಲೆನ್ ಪ್ರಕಟಿಸಯೆರ್, ಬುಕ್ಕೊ ಏಳ್ ಸಿರಿಲಿಕ್ ಬಾಸೆಡ್ ಪರ್ಷಿಯನ್ ಸಂಗ್ರಹೊಲು. ಅಂಚನೆ, ಆರ್ ಕೆಲವು ನಾಟಕೀಯ ಕೃತಿಲೆನ್ ಬರೆಯೆರ್, ಅವುಲು ಒಂಜಿ ಕೃತಿನ್ [[ದುಶಾನ್ಬೆ]] ಡ್ ೧೯೫೯ ಬುಕ್ಕೊ ೧೯೬೦ಡ್ ಒಪೆರಾ ಆದ್ ಪ್ರದರ್ಶನ ಮಲ್ತೆರ್.<ref name="EI" /><ref>{{cite book|last1=Farroḵzād|first1=Purān|title=Kārhā-ye zanān-e Irāni az diruz tā emruz |date=2002|location=Tehran|page=97}}</ref> ಇರಾನ್‌ದ ಇಸ್ಲಾಮಿಕ್ ಕ್ರಾಂತಿ ಸುರು ಆಯಿ ಬುಕ್ಕೊ, ಎಸ್ಫಹಾನಿ ಬುಕ್ಕೊ ಅರೆನ ಕುಟುಂಬ ೧೯೮೦ಡ್ ಇರಾನ್‌ಗ್ ಪಿರ ಬತ್ತೆರ್. ಎಸ್ಫಾಹಾನಿನ ಗಡಿಪಾರ್‌ದ ಪೊರ್ತುಡು, ಪರ್ವಿಜ್ ನಟೇಲ್-ಖಾನ್ಲಾರಿ ಅರೆನ ಕೆಲವು ಕಬಿತೆಲೆನ್ ಅರೆನ "ಸೋಖಾನ್" ಪತ್ರಿಕೆಡ್ ಪ್ರಕಟ ಮಲ್ತೆರ್. ಗಡಿಪಾರ್‌ಡ್ ಇತ್ತಿನ ಅರೆನ ಸುರುತ ಕಬಿತೆದ ಸಂಗ್ರಹ "ಜೆಂಡಾ ರುಡ್" 1965ಡ್ ಮಾಸ್ಕೋಡ್ ಪ್ರಕಟ ಆಂಡ್. ಈ ಸಂಗ್ರಹೊನು ಇರಾನ್‌ಡ್ ಒಂಜಿ ಎಲ್ಯ ಪ್ರಸರಣೊದ ಬುಕ್ಕೊ ನಿಷೇಧ ಮಲ್ತೆರ್, ಆಂಡ ಕ್ರಾಂತಿದ ಬುಕ್ಕೊ ೧೯೮೧ಡ್ ಟೆಹ್ರಾನ್‌ಡ್ ಪುನರ್ ಮುದ್ರಣ ಮಲ್ತೆರ್. ಅರೆನ ವಾಪಸ್ ಬನ್ನಗ ಅರೆನ ತೆಹ್ರಾನ್‌ಡ್ ``ಅಗರ್ ಹೆಜರ್ ಕಲಾಂ ದಷ್ಟಮ್'' (ಎಂಕ್ ಒಂಜಿ ಸಾವಿರ ಪೆನ್ ಇತ್ತ್ಂಡ) ಪ್ರಕಟ ಮಲ್ತೆರ್. ಅರೆನ ವಾಪಸ್ ಬತ್ತಿನ ರಡ್ಡ್ ವರ್ಷೊಡ್ದು ಬುಕ್ಕೊ, ತುಡೆಹ್ ಪಾರ್ಟಿನ್ ಇರಾನ್‌ಡ್ ಕುಡ ನಿಷೇಧ ಮಲ್ತೆರ್ ಬುಕ್ಕೊ ಎಸ್ಫಾಹಾನಿ ಬುಕ್ಕೊ ಅರೆನ ಕುಟುಂಬದಕುಲು ಕುಡ ಇಷ್ಟವಾಯಿನ ರೀತಿಡ್ ವಲಸೆ ಪೋಯೆರ್, ಈ ಸರ್ತಿ ಲಂಡನ್‌ಗ್, ಅಲ್ಪ ಅರೆನ ಬಾಕಿದ ಜೀವನೊನು ಕಳೆಯೆರ್.<ref name="EI" /><ref name="Guardian" /> == ಜೀವನೊದ ಪಿರವುದ ಜೀವನ ಬುಕ್ಕೊ ಮರಣೊ == ಲಂಡನ್‌ಡ್ ಜೀವನ ಮಲ್ಪುನಗ, ಎಸ್ಫಹಾನಿ ತನ್ನ ಸ್ವಂತ ಕೃತಿಲೆನ್ ಬರೆಯೆರೆ ತನ್ನ ಸಮಯೊನು ಮೀಸಲಾತ್‌ದ್, ಮೂಲ ಕಬಿತೆದ 10 ಸಂಪುಟೊಲೆನ್ ಬಿಡುಗಡೆ ಮಲ್ತೆರ್. ಅರೆನ ಆತ್ಮಚರಿತ್ರೆ '''ಸಾಯಾ-ಯೆ ಸಲಾಹಾ''' (ವರ್ಷೊಲೆನ ನೆರಳು) ೨೦೦೦ಡ್ ಬಿಡುಗಡೆ ಆಂಡ್.<ref name="Guardian" /> ೨೦೦೬ಡ್, ಎಸ್ಫಹಾನಿನ ಆಯ್ಕೆ ಮಲ್ತಿನ ಕಬಿತೆಲೆನ ಸುರುತ ಇಂಗ್ಲಿಷ್ ಅನುವಾದೊನು ಅರೆನ ಸ್ನೇಹಿತೆ '''ರುಹಿ ಸಾಹ್ಫಿ''' ಬರೆಯೆರ್, ಉಂದೆತ ಶೀರ್ಷಿಕೆ "ವಲಸೆ ಪಕ್ಕಿಲು".<ref name="Times" /> ನವೆಂಬರ್ ೨೯, ೨೦೦೭ಡ್, ಎಸ್ಫಾಹಾನಿ ಲಂಡನ್‌ಡ್ ೮೬ ವರ್ಸೊದ ಪ್ರಾಯೊಡು ತೀರ್ ಪೋಯೆರ್. ಆರ್ ಅರೆನ ರಡ್ಡ್ ಜೋಕುಲೆನ್ ಈ ಪ್ರಪಂಚೊಡು ಬುಡ್ದು ಪೋಯೆರ್.<ref name="Times" /><ref name="Guardian" /> == ಉಲ್ಲೇಕೊಲು == {{reflist}} [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ ಏಷ್ಯನ್ ತಿಂಗೊಲು 2024]] 6bt0cyq354cs5s3avqoxs2km6ixdlrq ಜಯಸುಧ ಕಪೂರ್ (ನಟಿ) 0 19445 366097 214535 2026-08-21T01:08:04Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366097 wikitext text/x-wiki '''ಜಯಸುಧ ಕಪೂರ್''' ಒರ್ತಿ ಭಾರತೀಯ ಚಲನಚಿತ್ರ ನಟಿ. ಮೇರ್ [[ತೆಲುಗು ಬಾಸೆ|ತೆಲುಗು]],[[ಕನ್ನಡ ಬಾಸೆ|ಕನ್ನಡ]],[[ತಮಿಳು ಬಾಸೆ|ತಮಿಳು]] ಅಂಚನೆ ಮಲಯಾಳ೦ ಬಾಸೆಲೆಡ್ ಅಬಿನಯ ಮಲ್ದೆರ್. ಮೇರ್ ೭ ರಾಜ್ಯ ನ೦ದಿ ಪ್ರಶಸ್ತಿ ಬುಕ್ಕ ೭ ದಕ್ಷಿಣ ಫಿಲ್ಮ್ಪೇರ್ ಪ್ರಶಸ್ತಿನ್ ಗೆಂದ್ದೆರ್.<ref>https://timesofindia.indiatimes.com/entertainment/hindi/bollywood/news/51st-Annual-Manikchand-Filmfare-Award-winners/etarticleshow/718496.cms?referral=PM</ref> == ಪುಟ್ಟು ಬುಕ್ಕ ವೃತ್ತಿಜೀವನ == ಜಯಸುಧ ೧೭ ಡಿಸೆ೦ಬರ್ ೧೯೫೮ ನೇ ತಾರೀಕ್‍ಗ್ ಮದ್ರಾಸ್‍ಡ್ ಪುಟ್ಟಿಯೆರ್.<ref>https://www.rediff.com/movies/2000/may/13jaya.htm</ref> ಅರೆನ ಚಿಕ್ಕಮ್ಮ ' ವಿಜಯಾ ನಿರ್ಮಲಾ' ತೆಲುಗು ಚಿತ್ರರ೦ಗೊದ ನಟಿ ಬುಕ್ಕ ನಿರ್ದೇಶಕಿ. ಮೇರ್ "ಪಯ೦ತಿತಿ ಕಪೂರ್" ಪನ್ಪಿನ ಸಿನಿಮೊಡ್ ತನ್ನ ಪದ್ರಾಡನೆ ಪ್ರಾಯೊಡ್ ಚಿತ್ರರ೦ಗೊಗ್ ಪ್ರವೇಶ ಮಲ್ತೆರ್. ನಿರ್ದೇಶಕ 'ಕೆ. ಬಾಲಚ೦ದರ್' ತಮಿಳು ಚಿತ್ರೊ ಆಯಿನ "ಅರ೦ಗೇತ್ರ" ಸಿನಿಮೊಡ್ ಮೆರೆಗ್ ಯಲ್ಯ ಪಾತ್ರೊನು ಕೊರಿಯೆರ್. ಮೇರ್ ೩೨೫ ಸಿನಿಮೊಲೆಡ್ ನಟನೆ ಮಲ್ದೆರ್. ಜಯಸುಧ ಅರೆನ ದೋಸ್ತಿ ಆಯಿನ ಜಯಪ್ರಧ ಅಂಚನೆ ರಾಧಿಕಾ ಅರೆನೊಟ್ಟುಗು "ಅಡವಿ ರಾಮುಡು" ಬುಕ್ಕ ಮೇಘಸ೦ದೇಷ೦" ಇಂಚ ಸುಮಾರ್ ಸಿನಿಮೊಲೆನ್ ಮಲ್ದೆರ್.<ref>{{Cite web |url=http://www.nilacharal.com/enter/interview/js.html |title=Archive copy |accessdate=2025-03-29 |archivedate=2006-11-05 |archive-url=https://web.archive.org/web/20061105210355/http://www.nilacharal.com/enter/interview/js.html |url-status=dead }}</ref><ref>https://web.archive.org/web/20090215013004/http://www.telugucinema.com/c/publish/stars/jayasudha.php</ref> == ಚಲನಚಿತ್ರೊಲು == === ಜಯಸುಧ ನಟನೆ ಮಲ್ದಿನ ಕನ್ನಡ ಸಿನಿಮೊಲು === {| class="wikitable" |- ! ವರ್ಸ!! ಚಿತ್ರೊ !! ಪಾತ್ರೊ |- | ೧೯೮೫ || ನೀ ತ೦ದ ಕಾಣಿಕೆ || ಮೀನಾ ದೇವಿ |- | ೨೦೦೪ || ಮೊ೦ಡ ||ಶಾರಾದ |- | ೨೦೦೭ || ತಾಯಿಯ ಮಡಿಲು || ಪೂರ್ಣಿಮ |- | ೨೦೧೫ || ವಜ್ರಕಾಯ ||ಲಕ್ಷ್ಮೀ |} ===ಜಯಸುಧ ನಟನೆ ಮಲ್ದಿನ ತೆಲುಗು ಸಿನಿಮೊಲು === {| class="wikitable" |- ! ಕ್ರ.ಸ೦ !! ಚಿತ್ರೊ !! ವರ್ಸ |- | ೧ || ತ್ರಿಪತಿ || ೧೯೭೪ |- | ೨ || ಸೊಗ್ಗಡು || ೧೯೭೫ |- | ೩ || ರಾಜು ವೇದಾಲೆ || ೧೯೭೬ |- | ೪ || ಮೊನಗಾಡು || ೧೯೭೬ |- | ೫|| ಅಲ್ಲುದೊಚಡು || ೧೯೭೬ |- | ೬ || ಜ್ಯೋತಿ || ೧೯೭೬ |- | ೭ ||ಪ್ರೇಮಲೇಖಲು || ೧೯೭೭ |- | ೮ ||ಮಲ್ಲೇ ಪೂವಲು || ೧೯೭೭ |- | ೯ || ಗಡುಸು ಅಮ್ಮಾಯಿ|| ೧೯೭೭ |- | ೧೦ ||ಗಡುಸು ಪಿಲ್ಲೋಡು || ೧೯೭೭ |- |೧೧ || ಅಮರದೀಪ೦ || ೧೯೭೭ |- | ೧೧ || ಆಮೆಕಥಾ || ೧೯೭೭ |- | ೧೨||ಆಡವಿ ರಾಮುಡು || ೧೯೭೭ |- | ೧೩ || ಶಿವರ೦ಜಿನಿ || ೧೯೭೮ |- | ೧೪ || ಲಾಯರ್ ವಿಶ್ವನಾಥ್ || ೧೯೭೮ |- | ೧೫ || ಕೆಡಿ ನ೦. ೧ || ೧೯೭೮ |- | ೧೬ || ಕಲಾ೦ತಕುಲು || ೧೯೭೮ |- | ೧೭|| ಪ್ರಾಣ೦ ಖರೀದು ||೧೯೭೮ |- | ೧೮ || ವಿಚಿತ್ರಾ ಜೀವಿತ೦ || ೧೯೭೮ |- | ೧೯ || ಯುಗ೦ದರ್ || ೧೯೭೯ |- | ೨೦ ||ಶ್ರಿ ತಿರುಪತಿ ವೆ೦ಕಟೆಶ್ವರ ಕಳ್ಯಾಣ೦ ||೧೯೭೯ |- | ೨೧ ||ಶಿವಮಟ್ಟಿನಾ ಸತ್ಯ೦ || ೧೯೭೯ |- |೧೨ || ಮುದ್ದುಲ ಕೊಡಕು || ೧೯೭೯ |- | ೧೩ || ಕಲ್ಯಾಣಿ || ೧೯೭೯ |- | ೧೪ || ಜುದಗಾಡು || ೧೯೭೯ |- | ೧೫ || ಇ೦ಟಿ೦ಟಿ ರಾಮಯಾಣ೦ || ೧೯೭೯ |- | ೧೬ ||ಡ್ರೈವರ್ ರಾಮುಡು ||೧೯೭೯ |- | ೧೭ || ಅ೦ದಮೈನಾ ಅನುಭವಮ್ || ೧೯೭೯ |- | ೧೮ || ಇದಿ ಕಥಾ ಕಾದು || ೧೯೭೯ |- | ೧೯ || ಗೋಪಾಲ ರಾವ್ ಗರಿ ಅಮ್ಮೈ || ೧೯೮೦ |- | ೨೦|| ಗೋಪಾಲ ಕೃಷ್ಣುಡು || ೧೯೮೦ |- | ೨೧ || ಗಜದೊ೦ಗ || ೧೯೮೦ |- | ೨೨ || ಪ್ರೇಮ ತಾರ೦ಗಾಲು || ೧೯೮೦ |- | ೨೩ || ಪ್ರೇಮಾಭಿಷೆಕ೦ || ೧೯೮೧ |- | ೨೪ || ಮಹಾ ಪುರುಶುಡು || ೧೯೮೧ |- | ೨೫ || ಇಲ್ಲಾಲು || ೧೯೮೧ |- | ೨೬ || ಅಗ್ನಿ ಪೂಲು || ೧೯೮೧ |- | ೨೭ || ಯುವರಾಜು || ೧೯೮೨ |- | ೨೮ || ರಾಗ ದೀಪ೦ || ೧೯೮೨ |- | ೨೯ || ನಾ ದೇಶ೦ || ೧೯೮೨ |- | ೩೦ || ಮಧುರ ಸ್ವಪ್ನ೦ || ೧೯೮೨ |- | ೩೧ || ಅನುರಾಗ ದೇವತಾ || ೧೯೮೨ |- | ೩೨ || ತ್ರಿಶೂಲ೦ || ೧೯೮೩ |- | ೩೩ || ಗೃಹಪ್ರವೇಶ೦ || ೧೯೮೨ |- | ೩೪ || ರಾಮುಡು ಕಾದು ಕೃಷ್ಣುಡು || ೧೯೮೩ |- | ೩೫ || ರಾಜ್ ಕುಮಾರ್ || ೧೯೮೩ |- | ೩೬|| ಪೊರ್ಥಮ್ || ೧೯೮೩ |- | ೩೭ || ಮೇಘಸ೦ದೇಶ೦ || ೧೯೮೩ |- | ೩೮ || ಯುಧ್ದ೦ || ೧೯೮೪ |- | ೩೯ || ಜಗನ್ || ೧೯೮೪ |- | ೪೦ || ಇದ್ದರು ದೊ೦ಗಲು || ೧೯೮೪ |- | ೪೧ || ಮಹಾ ಸ೦ಗ್ರಮ೦|| ೧೯೮೫ |- | ೪೨ || ಮಗಧಿರುಡು || ೧೯೮೬ |- | ೪೩|| ಕಾ೦ಚನ ಸೀತಾ || ೧೯೮೮ |- | ೪೪ || ಬಲೆ ದ೦ಪತಿಲು || ೧೯೮೯ |- |೪೫ || ವಿಜಯ್ || ೧೯೮೯ |- | ೪೬|| ಮನಿ || ೧೯೯೩ |- | ೪೭ || ತೊಡಿ ಕೊಡಲು || ೧೯೯೪ |- | ೪೮ ||ದೆಯ್ಯಾ೦ || ೧೯೯೬ |- | ೪೯||ಸುಪರ್ ಪೋಲಿಸ್ || ೧೯೯೪ |- | ೫೦ || ಡ್ಯಾಡಿ ಡ್ಯಾಡಿ || ೧೯೯೮ |- | ೫೧ || ಯುವಕುಡು || ೨೦೦೦ |- | ೫೨ || ಚಿನ್ನ || ೨೦೦೧ |- | ೫೩|| ವಿಷ್ಣು || ೨೦೦೩ |- | ೫೪ || ಬೊಮ್ಮಾರಿಲು || ೨೦೦೬ |- |೫೫ ||ವಿಜಯ ದಶಮಿ || ೨೦೦೭ |- | ೫೬|| ರಕ್ಷ || ೨೦೦೮ |- | ೫೭ || ಸೊಲೊ || ೨೦೧೧ |- | ೫೮ || ದೃವ <ref>https://web.archive.org/web/20120506160327/http://www.supergoodmovies.com/44148/tollywood/daruvu-overseas-by-bluesky-news-details</ref>|| ೨೦೧೩ |- | ೫೯ ||ಯವುಡು ||೨೦೧೪ |- | ಉದಾಹರಣೆ || ಶಾತಮಾನ೦ಭವತಿ ||೨೦೧೭ |- |} ==ಮಾನಾದಿಗೆಲು== * "ಪ್ರೈಡ್ ಆಪ್ ಇ೦ಡಿಯನ್ ಸಿನಿಮಾ ಅವಾರ್ಡ್ " ೨೦೦೭<ref>https://web.archive.org/web/20080511104141/http://www.bharatwaves.com/news/Jayasudha-receives-Pride-of-Indian-Cinema-Award-4625.html</ref> * ಅ೦ಧ್ರ ಪ್ರದೇಶ ಸರ್ಕಾರೊರ್ದ್ ೭ ನ೦ದಿ ಪ್ರಶಸ್ತಿ. * ೭ ಫಿಲ್ಮ್ಪೇರ್ ಪ್ರಶಸ್ತಿ. == ಉಲ್ಲೇಕೊಲು == [[ವರ್ಗೊ:ಸ್ತ್ರೀವಾದೊ ಬುಕ್ಕೊ ಜಾನಪದೊ 2025]] 4z4dvl73hags4irn67qrohbjx3yrqd7 ಜೇಮ್ಸ್ ಪೀಸ್ 0 19896 366098 359431 2026-08-21T01:09:28Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366098 wikitext text/x-wiki [[ಫೈಲ್:James Peace in the park in Wiesbaden.jpg|thumb|337x337px|ಜೇಮ್ಸ್ ಪೀಸ್]] '''ಕೆನ್ನೆತ್ ಜೇಮ್ಸ್ ಪೀಸ್''' (ಇಂಗ್ಲಿಸ್ ಬಾಸೆ: Kenneth James Peace), 1963 ಸೆಪ್ಟೆಂಬರ್ ತಿಂಗೊಳುದ 28ನೇ ಪೋಪುನಾನಿ ಜನ್ಮಿತೆರ್. ಸ್ಕೋಟ್‍ಲ್ಯಾಂಡ್‌ ದೇಶೊಡು ಸಂಗೀತ ನಿರ್ದೇಶಕೆರ್ ಆದಿತ್ತ್ ದ್ ಪಿಯಾನೊ ಅಂಚನೆ ದೃಶ್ಯ ಕಲೆಟ್ ಪುಗರ್ತೆ ಪಡೆಯಿನಾರ್. == ಜೀವನ == '''ಜೇಮ್ಸ್ ಪೀಸ್''' 1963 ಸೆಪ್ಟೆಂಬರ್ ತಿಂಗೊಳುದ 28ನೇ ತಾರೀಕ್ ದಾನಿ ಸ್ಕೋಟ್‍ಲ್ಯಾಂಡ್‌‌ದ ಪೈಸ್ಲೇಡ್ ಜನ್ಮಿತೆರ್.<ref name=":0">Birgitta Lampert. ꞌꞌKeine ꞌstachligenꞌ Klängeꞌꞌ. Wiesbadener Tagblatt (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ), ೧೦ ಫೆಬ್ರುವರಿ ೨೦೧೧</ref><ref name=":1">Julia Anderton. ꞌꞌTango als süß-saure Geschichteꞌꞌ. Wiesbadener Kurier (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ), ೨೪ ಮಾರ್ಚ್ ೨೦೧೨</ref> ಸ್ಕೋಟ್‍ಲ್ಯಾಂಡ್‌‌ದ ಪಡ್ಡೆಯಿ ದಿಕ್ಕ್‌ಡ್ ಇಪ್ಪುನ ಹೆಲೆನಸ್ ಬರ್‍ಗ್ ಪನ್ಪಿನ ಕಡಲ್ದ ಬರಿತ ಒಂಜಿ ವಿಹಾರ ತಾಣೊಡ್ ತನ್ನ ಜೋಕುಲಾಟಿಕೆದ ದಿನೊಕುಲೆನ್ ಕಳೆಯಿನಾರ್. ಕಲಾವಿದೆರ್‌ನ ಕುಟುಂಬೊಡು (ಜೋನ್ ಮಿಚ್ಗಿ, John McGhie) ಪುಟ್ಟಿನ ಜೇಮ್ಸ್, 20ನೇ ಶತಮಾನದ ಸುರುತ ದಿನೊಕುಲೆಡ್ ಸಂಗೀತ ಅಂಚನೆ ನೃತ್ಯೊಡು ಪುದರ್ ಪಡೆಯಿನ ಫೆಲಿಕ್ಸ್ ಬರ್‍ನ್ಸ್ (Felix Burns) ಪನ್ಪಿನಾರೆನ ಸಂಬಂಧಿಲಾ ಅಂದ್.<ref name=":0" /><ref name=":2">Sabine Klein. ꞌꞌMeine Musik ist wie ich - sehr romantisch ꞌꞌ. Frankfurter Rundschau (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ), ೧೯೯೨. ಸಂಚಿಕೆ ಸಂಖ್ಯೆ ೨೫೪, ಪುಟ ೨</ref> ತನ್ನ ಎನ್ಮನೇ ವರ್‍ಸೊಡ್ ಪಿಯಾನೊ ತರಬೇತಿ ಪಡೆದ್ 14ನೆ ವರ್‍ಸೊಡು ಸ್ಕೋಟ್ ಜೋಪ್ಲಿನ್‌ನ (Scott Joplin) ಒಟ್ಟಿಗೆ ಸುರುತ ವೇದಿಕೆ ಕಾರ್‍ಯಕ್ರಮ ಕೊರಿಯೆರ್. 16ನೇ ವರ್‍ಸೊಡು ರೋಯಲ್ ಸ್ಕೋಟಿಶ್ ಅಕಾಡೆಮಿ ಆಫ್ ಮ್ಯೂಸಿಕ್ ಆಂಡ್ ಡ್ರಾಮಾ (ಇತ್ತೆ ಅವು ರೋಯಲ್ ಕನ್ಸರ್‍ವೇಟಿವ್ ಆಫ್ ಸ್ಕೋಟ್‍ಲ್ಯಾಂಡ್‌ The Royal Conservatoire of Scotland ಆದುಂಡು) ಎಲ್ಯ ಪ್ರಾಯದ ಕಲ್ಪಾರ್‍ತಿ ಆದ್ ತರಬೇತಿಗ್ ಸೇರಿಯೆರ್.<ref name=":0" /><ref name=":1" /><ref name=":2" /><ref name=":3">G. Müller. ꞌꞌDie Seele des Klaviers tanzt tango ꞌꞌ. Kulturspiegel (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ), ೧೭ ವೆುೕ, ೨೦೦೧. ಪುಟ ೨</ref> 1983 ಗ್ಲಾಸ್ಗೋವ್ ವಿಶ್ವವಿದ್ಯಾಲಯೊರ್ದು (Glasgow University) ಪಿಯಾನೊ ವಿಷಯೊಡು ಬಿ ಎ ಪದವಿನ್ ಪಡೆಯೆರ್.<ref name=":3" /><ref name=":4">[https://portal.dnb.de/opac/simpleSearch?query=K.+James+Peace Deutsche Nationalbibliothek. ꞌꞌJames Peaceꞌꞌ]</ref> ಆರ್ ಎಸ್ ಎ ಎಂ ಡಿ ಆರ್‍ಕೆಸ್ಟ್ರಾಡ್ ಮೆಂಡಲ್ಸೋನ್ಸ್ ಪಿಯಾನೊ ಕನ್ ಸರ್‍ಟ್‌ಡ ಪಿಯಾನೊ ಬಾರಿಸಾದ್ ಡಿಪ್ಲೋಮಾ ಪದವಿನ್ ಪಡೆಯೆರ್.<ref name=":0" /><ref name=":5">ꞌꞌJames Peaceꞌꞌ. FRIZZ (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ). ಜನವರಿ, ೨೦೦೨, ಪುಟ ೫</ref> [[ಫೈಲ್:Cover page, Eternal Song Op.32 by James Peace (study score).png|left|thumb|306x306px|Eternal Song Op.32 by James Peace, the cover page]] ಕಲ್ಪಾತಿಗೆ ಕರಿನ ಬುಕ್ಕೊ ಜಿಂಜ ಬೇಡಿಕೆದ ಪಿಯಾನೊ ಕಲಾವಿದೆ ಆದ್ 1988ರ್ದ್ 1991 ಮುಟ್ಟ ಎಡಿನ್ ಬರ್ಗ್‌ಡ್ ಇತ್ತೆರ್.<ref name=":0" /><ref name=":1" /> 1991ರ್ದ್ 2009 ಮುಟ್ಟ [[ಜರ್ಮನಿ|ಜರ್ಮನ್‌‌ದ]] ಬಾಡ್ ನ್ಯೂಹೆಮ್‌ಡ್ (Bad Nauheim) ಇತ್ತ್‌ದ್ ಟಾಂಗೊ ಪನ್ಪಿನ ಸಂಗೀತ ಉಪಕರಣ ಕಲ್ತ್‌ದ್ ''ಟಾಂಗೋ ಎಸ್ಕೋಸ್'' (ಸ್ಕೋಟಿಶ್ ಟಾಂಗೊ, ꞌꞌTango estudioꞌꞌ) ಪನ್ಪಿನ ಸಿ ಡಿ ಬುಡುಗಡೆ ಮತ್ತೆರ್.<ref name=":2" /><ref name=":5" /><ref>Manfred Merz. ꞌꞌVirtuose, gefühlsbetonte Welt der Romantkꞌꞌ. Wetterauer Zeitung (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ). ೧೨ ಡಿಸೆಂಬರ್ ೧೯೯೨, ಪುಟ ೧೯</ref><ref name=":6">ꞌꞌJames Peaceꞌꞌ. The Tango Times (ನ್ಯೂಯಾರ್ಕ್ ತತಪತ್ರಿಕೆ), ಚಳಿಗಾಲದ ಸಂಚಿಕೆ ೨೦೦೨/೨೦೦೩. ಸಂಚಿಕೆ ಸಂಖ್ಯೆ ೩೯, ಪುಟಗಳು ೧‒೫</ref><ref name=":7">[https://search.nls.uk/primo-explore/fulldisplay?docid=44NLS_ALMA21615957400004341&context=L&vid=44NLS_VU1&lang=en_US&search_scope=SCOPE1&adaptor=Local%20Search%20Engine&query=any,contains,Tango%20escoc%C3%A9s&mode=basic National Library of Scotland. ꞌꞌTango escocèsꞌꞌ]</ref><ref name=":8">{{Cite web |url=https://pad.philharmoniedeparis.fr/pad/doc/ALOES/0795887/tango-escoces-scottish-tango-k-james-peace-comp-piano |title=Philharmonie de Paris. ꞌꞌTango escocésꞌꞌ |accessdate=2025-08-14 |archivedate=2025-09-28 |archive-url=https://web.archive.org/web/20250928140339/https://pad.philharmoniedeparis.fr/pad/doc/ALOES/0795887/tango-escoces-scottish-tango-k-james-peace-comp-piano |url-status=dead }}</ref><ref name=":9">[https://www.muziekweb.nl/Link/KHX3103/Tango-escoc%C3%A9s de bibliotheek. ꞌꞌTango escocésꞌꞌ Tango escocés - K. James Peace - Muziekweb]</ref> 2002ನೇ ಇಸವಿಡ್ ವಿಕ್ಟೋರಿಯಾ ಕಾಲೇಜ್ ಆಫ್ ಮ್ಯೂಸಿಕ್‌‌ದ ಮಾನಾದಿಗೆದ ಸದಸ್ಯೆ‌ರ್ ಆಯೆರ್‌.<ref name=":2" /><ref name=":6" /> ಅವ್ವೆ ವರ್‍ಸೊಡ್ ಉತ್ತರ [[ಜರ್ಮನಿ]]ಡ್ ಏಕವ್ಯಕ್ತಿ ಕಾರ್‍ಯಕ್ರಮೊಲೆನ್ ಕೊರೊಂದು ಹಾಂಕಾಂಗ್‌ಡ್ ಟಾಂಗೊ 17 ಪನ್ಪಿನ ಕಾರ್‍ಯಕ್ರಮ ಕೊರಿಯೆರ್.<ref name=":6" /><ref name=":7" /><ref name=":8" /><ref name=":9" /><ref>La Cadena (ಡಚ್ ನಿಯತಕಾಲಿಕೆ). ಸೆಪ್ಟೆಂಬರ್ ೨೦೦೨, ಪುಟ ೨೬</ref><ref>TangoTang (ಸುದ್ದಿಪತ್ರ). ಹಾಂಗ್ ಕಾಂಗ್, ೮ ಅಕ್ಟೋಬರ್ ೨೦೦೨</ref><ref>[https://www.scmp.com/article/393890/james-peace ꞌꞌJames Peaceꞌꞌ. South China Morning Post (ಹಾಂಕಾಂಗ್ ತತಪತ್ರಿಕೆ). ೯ ಅಕ್ಟೋಬರ್ ೨೦೦೨]</ref> ಅಯಿರ್ದ್ ಬುಕ್ಕೊದ ದಿನೊಕ್‌ಲೆಡ್ ಅರೆನ ಕಾರ್‍ಯಕ್ರಮೊಕುಲು ಯುರೋಪ್ ದೇಶೊಡ್ ಜಾಸ್ತಿ ಆದ್ ನಡತೊಂದು ಬತ್ತ್ಂಡ್. ಅಮೆಸ್ಟರ್‌ಡಾಂ, ಆಥೆನ್ಸ್,<ref>ಸಂಗೀತ ಕಾರ್ಯಕ್ರಮದ ಕರಪತ್ರ ''{Για σένα, Αγγελική}''. ಅಥೆನ್ಸ್, ೨೭ ಅಕ್ಟೋಬರ್, ೨೦೧೬</ref> ಬೆರ್‍ಲಿನ್,<ref>Tangodanza (ಜರ್ಮನ್ ತಪತ್ರಿಕೆ). ಸಂಚಿಕೆ ಸಂಖ್ಯೆ ೧/೨೦೦೨, ಪುಟ ೯</ref> ಬ್ರುಸೆಲ್ಸ್, ಹೆಲ್ಸಿಂಕಿ,<ref>ಸಂಗೀತ ಕಛೇರಿಯ ಪೋಸ್ಟರ್ (ಫಿನ್ಲೆಂಡ್ ಸಂಗೀತ ಪ್ರದರ್ಶನ ಪ್ರವಾಸ, ೨೦೧೪)</ref> ಲಿಸ್ಬೊನ್,<ref>ಸಂಗೀತ ಕಛೇರಿಯ ಪೋಸ್ಟರ್ (ಪೋರ್ಚುಗಲ್ ಸಂಗೀತ ಪ್ರದರ್ಶನ ಪ್ರವಾಸ, ೨೦೧೬)</ref> ಲಂಡನ್, ಮಡ್ರಿಡ್,<ref>ಸಂಗೀತ ಕಛೇರಿಯ ಪೋಸ್ಟರ್, ꞌꞌ¡Feliz cincuenta cumpleaños - 2013!ꞌꞌ (ಸ್ಪೇನ್ ಸಂಗೀತ ಪ್ರದರ್ಶನ ಪ್ರವಾಸ)</ref> ಒಸ್ಲೊ,<ref>{{Cite web |url=https://listen.no/event/konsert-kl-13-00-james-peace-flygel-tangos-romanticos-musikk-av-peace-piazzolla-albeniz-utstillingssalene-er-stengt |title=Listen.no.: Konsert, ಜೇಮ್ಸ್ ಪೀಸ್ (ಪಿಯಾನೋ). Munch Museum (ಓಸ್ಲೋ). ೧೬ ಅಕ್ಟೋಬರ್ ೨೦೦೪ |accessdate=2025-08-14 |archivedate=2020-06-08 |archive-url=https://web.archive.org/web/20200608114526/https://listen.no/event/konsert-kl-13-00-james-peace-flygel-tangos-romanticos-musikk-av-peace-piazzolla-albeniz-utstillingssalene-er-stengt |url-status=dead }}</ref> ರೆಕ್ಜಾವಿಕ್<ref>[https://www.mbl.is/greinasafn/grein/823608/ Ríkarður Ö. Pálsson. “Skozir Slaghörputangoár”. Morgunblaðið (mbl). ರೀಕಾವಿಕ್, ೧೪ ಅಕ್ಟೋಬರ್ ೨೦೦೪]</ref> ಬುಕ್ಕೊ ವಿಯೆನ್ನಾ<ref>ಸಂಗೀತ ಕಾರ್ಯಕ್ರಮದ ಕರಪತ್ರ. ವಿಯೆನ್ನಾ. ೨೩ ಜನವರಿ, ೨೦೦೫</ref> ಇಂಚಿತ್ತಿ ಜಾಗೆಲೆಡ್ ನಡತ್ತ್ಂಡ್. 2008ನೆ ವರ್‍ಸೊಡ್ ಟಾಂಗೋದ ಸಂಗೀತ ಸೇವೆನ್ ಗುರುತಿಸಾದ್ ಲಂಡನ್ ಕಾಲೇಜ್ ಆಫ್ ಮ್ಯೂಸಿಕ್ ಸಂಸ್ಥೇಡ್ ಮಾನಾದಿಗೆದ ಸದಸ್ಯತನ ಕೊರಿಯೆರ್.<ref name=":10">[https://search.nls.uk/primo-explore/fulldisplay?vid=44NLS_VU1&tab=default_tab&docid=44NLS_ALMA21438757900004341&lang=en_US&context=L&adaptor=Local%20Search%20Engine&query=any,contains,K.%20James%20Peace&mode=basic National Library of Scotland. ꞌꞌK. James Peace in Wiesbadenꞌꞌ]</ref><ref name=":11">[https://portal.dnb.de/opac.htm?method=simpleSearch&cqlMode=true&query=idn%3D1022200380 Deutsche Nationalbibliothek. ꞌꞌK. James Peace in Wiesbadenꞌꞌ]</ref><ref name=":12">[https://whittakerlive.blogspot.com/2012/05/new-dvd-interview-with-wiesbaden.html Whittaker Live. ꞌꞌWiesbaden Highlander K. James Peace, tango composer: New DVD]</ref> ಎಡಿನ್ ಬರ್ಗ್‌ಡ್ ವಂತೆ ಸಮಯ ಇತ್ತ್‌ದ್ 2010 ಬುಕ್ಕೊ [[ಜರ್ಮನಿ]]ದ ವಿಯಸ್ಬೇಡನ್‌ಡ್ ಉಲ್ಲೆರ್.<ref name=":0" /><ref name=":2" /> ಮುಲ್ಪ ಅರೆನ ರಚನಾತ್ಮಕ ಕೆಲಸೊಲು ದುಂಬರಿದ್ ಕಿರು ಚಿತ್ರ ತಯಾರ್ ಮಲ್ತೆರ್. ''ಜೇಮ್ಸ್ ಪೀಸ್ ಇನ್ ವಿಯಾಸ್‌‌ಬಡೆನ್ꞌ'' (ꞌꞌK. James Peace in Wiesbadenꞌꞌ) ಪನ್ಪಿನ ಸಾಕ್ಷ್ಯ ಚಿತ್ರ ಲಾ ಮಲ್ತೆರ್.<ref name=":10" /><ref name=":11" /><ref name=":12" /> [[ಫೈಲ್:James Peace - Idylls Op.4b.webm|center|thumb|390x390px|James Peace - Idylls Op.4b]] == ಬಹುಮಾನ ಬುಕ್ಕೊ ಮಾನಾದಿಗೆ == ●     ಸುರುತ ಬಹುಮಾನ, "ಆಗ್ನೆಸ್ ಮಿಲ್ಲರ್" ಸ್ಪರ್ಧೆ ಗ್ಲಾಸ್ಗೊ (Agnes Millar Prize for sight-reading), Glasgow, 1983<ref name=":3" /> [[ಫೈಲ್:Tango Milonga op. 26.wav|thumb|Tango Milonga op. 26 by James Peace]] ●     ಸುರುತ ಬಹುಮಾನ, "ಡನ್ಬಾರ್‌ಟೋನ್ಸೈರ್ ಇಐಸ್" ಗ್ಲಾಸ್ಗೊ (Dunbartonshire E.I.S. Prize for piano accompaniment), Glasgow, 1984<ref name=":3" /> [[ಫೈಲ್:Tango XVIII by James Peace (James Peace, piano).wav|thumb|Tango XVIII by James Peace (James Peace, piano)]] ●     ಸಿಬೆಲಿಯಸ್ ಎಸ್ಸೆ ಬಹುಮಾನ ಗ್ಲಾಸ್ಗೊ, Glasgow 1995<ref name=":3" /> ●     ಮಾನಾದಿಗೆದ ಡಿಪ್ಲೊಮ ಟಿಐಎಂ ಇಂಟರ್ ನ್ಯಾಷನಲ್ ಕಂಪೋಸಿಶನ್ ಕಾಂಪಿಟೀಶನ್ ರೋಮ್ (Torneo Internazionale di Musica), Rome, 2000<ref name=":0" /><ref name=":1" /><ref name=":4" /> ●     ಮಾನಾದಿಗೆದ ಡಿಪ್ಲೊಮ ಐಬಿಎಲ್‌ಏ ಫೌಂಡೇಶನ್ ನ್ಯೂಯೋರ್ಕ್, New York 2002<ref name=":0" /><ref name=":1" /><ref name=":4" /> ●     ಕಮ್ಮೊಮರೇಟಿವ್ ಮೆಡಲ್ ಇಂಟರ್ ನ್ಯಾಷನಲ್ ಪಿಯಾನೊ ಡ್ಯೊ ಅಸೋಸಿಯೇಶನ್‌ ಟೋಕ್ಯೋ, Tokyo, 2002<ref name=":0" /><ref name=":1" /><ref name=":4" /><ref name=":10" /><ref name=":11" /><ref name=":12" /><ref>{{Cite web |url=http://ipda-pianoduo.com/composition-concour-list-english.html |title=International Piano Duo Association (ಟೋಕಿಯೊ). List of winners, ೨೦೦೨ |accessdate=2025-08-14 |archivedate=2020-05-27 |archive-url=https://web.archive.org/web/20200527164059/http://www.ipda-pianoduo.com/composition-concour-list-english.html |url-status=dead }}</ref> ●     ಗೋಲ್ಡ್ ಮೆಡಲ್ ಇಂಟರ್ ನ್ಯಾಷನಲ್ ಅಕಾಡೆಮಿ ಆಫ್ ಲ್ಯುಟೆಸ್ (Internationale Académie de Lutèce), ಪ್ಯಾರಿಸ್, Paris, 2005<ref name=":0" /><ref name=":1" /> == ಮುಖ್ಯ ಸಂಗೀತ ನಿರ್ದೇಶೆನೊಲು == ●     ದಿ ವಾಟರ್ ಫಾಲ್ (The Waterfall)<ref>Staatstheater Wiesbaden (ಸಂಗೀತ ಕಾರ್ಯಕ್ರಮದ ಕರಪತ್ರ). ೧೨/೧೯ ಸೆಪ್ಟೆಂಬರ್, ೨೦೨೧</ref> ●     ಐಡಿಲ್ಸ್ (Idylls) ●     ಸೈಲೆಂಟ್ ಟಿಯ‌ರ್‍ಸ್ (Silent Tears) ●     ಫೋರ್‍ಗೋಟನ್ ಲೀವ್ಸ್ (Forgotten Leaves) ●     ಸೋನಾಟ ಪಾರ್ ಒಬೊಯೆ ಆಂಡ್ ಪಿಯಾನೊ (Oboe Sonata) ●     ಬ್ಯಾಲಡ್ (Ballade) ●     ಸೆರೆಮೋನಿಯಲ್ ಮಾರ್ಚ್ ನಂ 1 (Ceremonial March No.1) ●     ಸೆರೆಮೋನಿಯಲ್ ಮಾರ್ಚ್ ನಂ 2 (Ceremonial March No.2) ●     ಆಟಮನ್ ಗೋಲ್ಡ್ (Autumn Gold)<ref>Schwäbische Post. ꞌꞌVioline schwebt über Orchesterklangꞌꞌ. ೪ ಜೂನ್, ೧೯೯೪</ref> ●     ಎಟರ್‌ನಲ್ ಸೋಂಗ್ (Eternal Song)<ref name=":0" /> ●      ಪಾರ್ ಜೊರ್‍ಜಿಯ (ಜೋರ್‍ಜಿಯ ಭಾಸೆಡ್: საქართველოსთვის) (ಸಾಹಿತ್ಯೊ: ತಮರಿ ಚಿಕವೆಡ್ಸೆ , ಝರಬಿ ಚಿಕವೆಡ್ಸೆ ಬುಕ್ಕೊ ಜೇಮ್ಸ್ ಪೀಸ್) ●     24 ಟಾಂಗೋಸ್ ಸೊಲೊ ಪಿಯನೊ<ref name=":0" /><ref name=":6" /><ref name=":7" /><ref name=":8" /><ref name=":9" /><ref name=":10" /><ref name=":11" /><ref name=":12" /><ref>[https://www.britishviolasociety.co.uk/wp-content/uploads/2024/01/Latest-news-from-the-British-Viola-Society.-August-2023-compressed.pdf British Viola Society. Newsletter, August 2023]</ref> == ಮರ್ಗಿಲ್‌ದ ವಿಷಯೊಲು == ●   [https://www.youtube.com/watch?v=ZcMRNnwNJhk Lento Lacrimoso - ''www.youtube.com''] ●   [https://www.youtube.com/watch?v=UVRnWXsqxZE Souvenir de Buenos Aires - ''www.youtube.com''] ●   [https://www.youtube.com/watch?v=_VKxG2dmQn4 Autumn Gold - ''www.youtube.com''] == ಉಲ್ಲೇಕೊಲು == fkn5zw59x169pk5rgkq5vpqxfxg2ozv ಕ್ಯೂಬಾ 0 22031 366096 363748 2026-08-21T00:53:35Z InternetArchiveBot 4316 Rescuing 0 sources and tagging 1 as dead.) #IABot (v2.0.9.5 366096 wikitext text/x-wiki '''ಕ್ಯೂಬಾ''', ಅಧಿಕೃತವಾಗಿ "ರಿಪಬ್ಲಿಕ್ ಆಫ್ ಕ್ಯೂಬಾ", ಕೆರಿಬಿಯನ್ ಸಮುದ್ರದ ಒಂಜಿ ದ್ವೀಪ ರಾಷ್ಟ್ರ ಆಯಿತು. ಇತ್ತೆ 4,195 ದ್ವೀಪು, ಚಿಕ್ಕ ದ್ವೀಪು, ಬಂಡೆಗಡ್ದು ಇತ್ತೆ, ಇತ್ತೆ ತಾನೆ "ಇಸ್ಲಾ ಡೆ ಲಾ ಜುವೆಂಟುಡ್" ಬುಕ್ಕೊಾ ಮುಖ್ಯ ದ್ವೀಪು. ಕ್ಯೂಬಾ, ಕೆರಿಬಿಯನ್ ಸಮುದ್ರ, ಮೆಕ್ಸಿಕೋ ಕೊಲ್ಲಿ, ಅಟ್ಲಾಂಟಿಕ್ ಮಹಾಸಾಗರದ ಸಂಗಮದ ತಡೆ ಇತ್ತೆ. ಇತ್ತೆ ಯುಕಟಾನ್ ಪ್ರायद್ವೀಪದ ಪೂರ್ವ, ಫ್ಲೋರಿಡಾ ಬುಕ್ಕೊಾ ಬಹಾಮಾಸ್ ದಕ್ಷಿಣ, ಹೈಟಿ ಬುಕ್ಕೊಾ ಡೊಮಿನಿಕನ್ ರಿಪಬ್ಲಿಕ್ ಪಶ್ಚಿಮ, ಜಮೈಕಾ ಬುಕ್ಕೊಾ ಕೇಮನ್ ದ್ವೀಪುಗಳ ಉತ್ತರದ ತಡೆ ಇತ್ತೆ. ಹವಾನಾ ಇತ್ತೆ ದೊಡ್ಡ ನಗರ ಬುಕ್ಕೊಾ ರಾಜಧಾನಿ. ಕ್ಯೂಬಾ, ಹೈಟಿ ಬುಕ್ಕೊಾ ಡೊಮಿನಿಕನ್ ರಿಪಬ್ಲಿಕ್ ತಡಿಯೆ, ಕೆರಿಬಿಯನ್ ಪ್ರದೇಶದ ಮೂವತ್ತನೆ ಜನಸಂಖ್ಯೆ ಇತ್ತೆ, ಸುಮಾರು 10 ಮಿಲಿಯನ್ ಜನ ಇತ್ತೆ. ಭೂಪ್ರದೇಶದ ತಡೆ, ಕ್ಯೂಬಾ ಕೆರಿಬಿಯನ್ ಪ್ರದೇಶದ ದೊಡ್ಡ ರಾಷ್ಟ್ರ ಆಯಿತು. ಸಂಸ್ಕೃತಿಯ ತಡೆ, ಲ್ಯಾಟಿನ್ ಅಮೆರಿಕದ ಭಾಗ ಆಯಿತು.<ref>{{Cite book|url=http://archive.org/details/modernlatinameri0006skid|title=Modern Latin America|last=Skidmore|first=Thomas E.|date=2005|publisher=New York : Oxford University Press|others=Internet Archive|isbn=978-0-19-517012-2}}</ref> == ಇತಿಹಾಸ == ಕ್ಯೂಬಾ BC ನಾಲ್ಕನೆ ಸಾವಿರ ವರ್ಷ ತಡೆಯಿಂದ ಮನುಷ್ಯರು ವಾಸ ಮಾಡ್ದು. ಗುವಾನಹಟಬೆ ಬುಕ್ಕೊಾ ತೈನೋ ಜನಾಂಗದವರು 15ನೆ ಶತಮಾನ ತಡೆ ಸ್ಪೇನ್ ಜನಾಂಗದ ಕಾಲೊನೀಕರಣದ ಕಾಲದ ತಡೆ ಇತ್ತೆ. 1492 ಅಕ್ಟೋಬರ್ 27ನೆ ತಾರೀಕು ಕ್ರಿಸ್ಟೋಫರ್ ಕೊಲಂಬಸ್ ಕ್ಯೂಬಾದ ತಡೆಗೆ ಬಂದು, ಇತ್ತೆ "ಇಸ್ಲಾ ಜುವಾನಾ" ಎಂತ್ ಹೆಸರು ಇತ್ತೆ. 1511 ತಡೆ ಡಿಯೆಗೋ ವೆಲಾಸ್ಕೆಜ್ ಡೆ ಕುಯೆಲ್ಲಾರ್ ಬಾರಾಕೋವಾ ತಡೆ ಸ್ಪೇನ್ ಕಾಲೊನಿ ಸ್ಥಾಪನೆ ಮಾಡ್ದು. 1514 ತಡೆ ಹವಾನಾ ಸ್ಥಾಪನೆ ಆಯಿತು, 1607 ತಡೆ ರಾಜಧಾನಿ ಆಯಿತು. ತೈನೋ ಜನಾಂಗದವರು ಎಂಕೊಮಿಯೆಂಡಾ ಪದ್ಧತಿಯ ತಡೆಯಲ್ಲಿ ಕೆಲಸ ಮಾಡ್ದು, ಇತ್ತೆ ಯೂರೋಪ್ ಕಾಲದ ಫ್ಯೂಡಲ್ ಪದ್ಧತಿಯ ತಡೆ ಇತ್ತೆ. ಇತ್ತೆ ಜನಾಂಗದವರು ಬಾಯಿಲು ಕಾಯಿಲೆ, ಕಠಿಣ ಪರಿಸ್ಥಿತಿ, ಬುಕ್ಕೊಾ ಶೋಷಣೆಯ ತಡೆ ಮರಣಪಟ್ಟು. 1529 ತಡೆ ಮಿಸಿಲ್ಸ್ ಕಾಯಿಲೆ ತಡೆಯಲ್ಲಿ ಬಹುಮಂದಿ ತೈನೋ ಜನಾಂಗದವರು ಮರಣಪಟ್ಟು. 1898 ತಡೆ "ಸ್ಪ್ಯಾನಿಷ್-ಅಮೆರಿಕನ್ ಯುದ್ಧ" ಮುಗಿದ್, "ಪ್ಯಾರಿಸ್ ಒಪ್ಪಂದ" ಮೂಲಕ ಸ್ಪೇನ್ ಕ್ಯೂಬಾದ ಮೇಲೆ ತನ್ನ ಹಕ್ಕು ತ್ಯಜನೆ. 1902 ಮೇ 20 ತಡೆ, ಕ್ಯೂಬಾ ಅಧಿಕೃತವಾಗಿ ಸ್ವಾತಂತ್ರ್ಯ ಪಡ್ದು, "ರಿಪಬ್ಲಿಕ್ ಆಫ್ ಕ್ಯೂಬಾ" ಘೋಷಣೆ ಆಯಿತು. ಆದರೆ "ಪ್ಲಾಟ್ ತಿದ್ದುಪಡಿ" ತಡೆ, ಅಮೆರಿಕ ಕ್ಯೂಬಾದ ಆಂತರಿಕ ವಿಷಯಗಳಲ್ಲಿ ಹಸ್ತಕ್ಷೇಪದ ಹಕ್ಕು ಇತ್ತೆ.<ref name=":0">{{Cite web|title=Republic of Cuba {{!}} Cuban history [1902–1959] {{!}} Britannica|url=https://www.britannica.com/place/Republic-of-Cuba-1902-1959|website=www.britannica.com|accessdate=2025-11-02|language=en}}</ref> 1906 ತಡೆ, ಚುನಾವಣೆ ವಿವಾದದ ತಡೆ ಅಮೆರಿಕ ಸೇನೆ ಕ್ಯೂಬಾದಲ್ಲಿ ಪ್ರವೇಶ ಮಾಡ್ದು. 1908 ತಡೆ, ಜೋಸೆ ಮಿಗುಯೆಲ್ ಗೊಮೆಜ್ ಅಧ್ಯಕ್ಷ ಆಯಿದು. 1924 ತಡೆ, ಜೆರಾರ್ಡೋ ಮಚಾಡೋ ಅಧ್ಯಕ್ಷ ಆಯಿದು; ಇತ್ತೆ ಕಾಲದ ತಡೆ ಪ್ರವಾಸೋದ್ಯಮ ಹೆಚ್ಚಿದ್ದು, ಜೂಜು ಬುಕ್ಕೊಾ ವೇಶ್ಯಾವೃತ್ತಿ ಕೂಡ ಹೆಚ್ಚಾಯಿದು.<ref name=":0" /> 1933 ತಡೆ, ಫುಲ್ಜಿencio ಬಾಟಿಸ್ಟಾ ಎಂತ್ ಸರ್ಜೆಂಟ್ ಹೋರಾಟದ ತಡೆ ಸರ್ಕಾರದ ವಿರುದ್ಧ ತಿರುಗುಬುಡ್ದು. ಇತ್ತೆ ಕಾಲದ ತಡೆ ರಾಜಕೀಯ ಅಸ್ಥಿರತೆ ಇತ್ತೆ. 1940 ತಡೆ, ಹೊಸ ಸಂವಿಧಾನ ರೂಪಾಯಿದು, ಬಾಟಿಸ್ಟಾ ಅಧ್ಯಕ್ಷ ಆಯಿದು. 1944 ತಡೆ, ರಾಮೋನ್ ಗ್ರಾವ್ ಸಂ ಮಾರ್ಟಿನ್, 1948 ತಡೆ ಕಾರ್ಲೋಸ್ ಪ್ರಿಯೋ ಸೋಕಾರಾಸ್ ಅಧ್ಯಕ್ಷರಾಗಿ ಆಯಿದು<ref name=":0" />. 1952 ತಡೆ, ಬಾಟಿಸ್ಟಾ ಮತ್ತೆ ಸೇನೆ ಮೂಲಕ ಅಧಿಕಾರಕ್ಕೆ ಬಂದು, 1940 ಸಂವಿಧಾನ ರದ್ದು ಮಾಡ್ದು. ಇತ್ತೆ ಕಾಲದ ತಡೆ ರಾಜಕೀಯ ಹಕ್ಕುಗಳು ಕಡಿತ ಆಯಿದು, ಆರ್ಥಿಕ ಅಸ್ಥಿರತೆ ಹೆಚ್ಚಾಯಿದು. 1953–1959 ತಡೆ, ಫಿಡೆಲ್ ಕ್ಯಾಸ್ಟ್ರೋ ನೇತೃತ್ವದ "ಜುಲೈ 26 ಚಲನೆ" ಬಾಟಿಸ್ಟಾ ವಿರುದ್ಧ ಕ್ರಾಂತಿ ನಡೆಸಿದ್ದು, 1959 ಜನವರಿ 1 ತಡೆ ಬಾಟಿಸ್ಟಾ ರಾಜೀನಾಮೆ ಕೊಟ್ಟು ದೇಶ ಬಿಟ್ಟು ಹೋಗ್ದು. ಫಿಡೆಲ್ ಕ್ಯಾಸ್ಟ್ರೋ ಹವಾನಾ ತಡೆ ಪ್ರವೇಶ ಮಾಡ್ದು, ಕ್ರಾಂತಿ ಯಶಸ್ವಿ ಆಯಿದು.<ref>{{Cite book|url=https://books.google.com/books?id=zPDFHE_5besC&pg=PA262|title=World Literature in Spanish: An Encyclopedia [3 volumes]: An Encyclopedia|last=Ihrie|first=Maureen|last2=Oropesa|first2=Salvador|date=2011-10-20|publisher=ABC-CLIO|isbn=978-0-313-08083-8|language=en}}</ref> 1959 ತಡೆ, ಫಿಡೆಲ್ ಕ್ಯಾಸ್ಟ್ರೋ ನೇತೃತ್ವದ ಕ್ರಾಂತಿ ಯಶಸ್ವಿಯಾಗಿ ಬಾಟಿಸ್ಟಾ ಸರ್ಕಾರವನ್ನು ತಿರಸ್ಕರ್ದು. ಕ್ಯಾಸ್ಟ್ರೋ ಅಧ್ಯಕ್ಷನಾಗಿ ಅಧಿಕಾರಕ್ಕೆ ಬಂದು, ಕ್ಯೂಬಾ ಕಮ್ಯುನಿಸ್ಟ್ ಪಕ್ಷದ ನೇತೃತ್ವದಲ್ಲಿ ಹೊಸ ಸರ್ಕಾರ ಸ್ಥಾಪನೆ ಆಯಿದು.<ref>{{Cite web|title=Cuban Revolution {{!}} Summary, Facts, Causes, Effects, & Significance {{!}} Britannica|url=https://www.britannica.com/event/Cuban-Revolution|website=www.britannica.com|date=2025-10-01|accessdate=2025-11-02|language=en}}</ref> '''ಅಮೆರಿಕದ ಸಂಬಂಧ''': ಕ್ರಾಂತಿಯ ನಂತರ, ಅಮೆರಿಕ-ಕ್ಯೂಬಾ ಸಂಬಂಧ ತೀವ್ರವಾಗಿ ಕೆಡಿದು. 1960–1964 ತಡೆ, ಅಮೆರಿಕ ಕ್ಯೂಬಾ ವಿರುದ್ಧ ವ್ಯಾಪಾರ ನಿಷೇಧ, ಆಸ್ತಿ ತಡೆ, ಬುಕ್ಕೊಾ ರಾಜತಾಂತ್ರಿಕ ಒಪ್ಪಂದ ರದ್ದು ಮಾಡ್ದು.<ref name=":1">{{Cite web|title=Cuban Revolution - 1959, Timeline & Summary|url=https://www.history.com/articles/cuban-revolution|website=HISTORY|date=2021-08-19|accessdate=2025-11-02|language=en|first=HISTORY com|last=Editors}}</ref> '''ಬೇ ಆಫ್ ಪಿಗ್ಸ್ ದಾಳಿ''': 1961 ತಡೆ, ಅಮೆರಿಕ ಬೆಂಬಲಿತ ಕ್ಯೂಬಾ ಶರಣಾರ್ಥಿಗಳ ತಂಡ "ಬ್ರಿಗೇಡ್ 2506" ಕ್ಯೂಬಾದ ಮೇಲೆ ದಾಳಿ ಮಾಡ್ದು. ಕ್ಯೂಬಾ ಸೇನೆ ದಾಳಿಯನ್ನು ತಿರಸ್ಕರ್ದು, ದಾಳಿ ವಿಫಲ ಆಯಿದು.<ref name=":1" /> '''ಕ್ಯೂಬಾ ಕ್ಷಿಪಣಿ ಸಂಕಷ್ಟ''': 1962 ತಡೆ, ಸೋವಿಯತ್ ಯೂನಿಯನ್ R-12 ಕ್ಷಿಪಣಿಗಳನ್ನು ಕ್ಯೂಬಾದಲ್ಲಿ ಸ್ಥಾಪನೆ ಮಾಡ್ದು. ಇತ್ತೆ "ಕ್ಯೂಬಾ ಮಿಸೈಲ್ ಕ್ರೈಸಿಸ್" ಎಂತ್ ಪ್ರಸಿದ್ಧ ಆಯಿದು, ಜಗತ್ತನ್ನು ಅಣುಯುದ್ಧದ ಅಂಚಿಗೆ ತಂದು<ref name=":1" />. '''ಆಫ್ರಿಕಾದಲ್ಲಿ ಹಸ್ತಕ್ಷೇಪ''': 1970–1980 ತಡೆ, ಕ್ಯೂಬಾ ಆಫ್ರಿಕಾದಲ್ಲಿ ಹಲವಾರು ಯುದ್ಧಗಳಲ್ಲಿ ಭಾಗವಹಿಸಿದು. ''ಅಂಗೋಲಾ'', ''ಇಥಿಯೋಪಿಯಾ'', ''ಮೊಜಾಂಬಿಕ್'', ''ಗಿನಿ-ಬಿಸ್ಸೌ'' ಮುಂತಾದ ದೇಶಗಳಲ್ಲಿ ಕ್ಯೂಬಾ ಸೇನೆ, ವೈದ್ಯಕೀಯ ಸಹಾಯ, ಬುಕ್ಕೊಾ ತರಬೇತಿ ನೀಡಿದ್ದು.<ref>{{Citation|title=Cuba and the Cold War, 1959–1980|url=https://www.cambridge.org/core/books/cambridge-history-of-the-cold-war/cuba-and-the-cold-war-19591980/95B67DF793E8113AFD02B61BA7A4D439|publisher=Cambridge University Press|work=The Cambridge History of the Cold War: Volume 2: Crises and Détente|date=2010|accessdate=2025-11-02|isbn=978-0-521-83720-0|pages=327–348|volume=2|series=The Cambridge History of the Cold War|first=Piero|last=Gleijeses|editor-first=Melvyn P.|editor-last=Leffler}}</ref> '''ಮಧ್ಯಪ್ರಾಚ್ಯ ಮತ್ತು ಲ್ಯಾಟಿನ್ ಅಮೆರಿಕ''': ಕ್ಯೂಬಾ ''ಸಿರಿಯಾ'', ''ಇರಾಕ್'', ''ದಕ್ಷಿಣ ಯೆಮೆನ್'' ಮುಂತಾದ ದೇಶಗಳಿಗೆ ಸಹಾಯ ನೀಡಿದ್ದು. ಲ್ಯಾಟಿನ್ ಅಮೆರಿಕದ ''ನಿಕಾರಾಗುವಾ'', ''ಬೊಲಿವಿಯಾ'' ಮುಂತಾದಲ್ಲಿ ಬಂಡಾಯ ಚಟುವಟಿಕೆಗಳಿಗೆ ಬೆಂಬಲ ಕೊಡುಪೆನೆ.<ref>{{Cite web|title=Cuba and the Cold War: A Historical Perspective|url=https://www.mexicohistorico.com/paginas/cuba-and-the-cold-war-a-historical-perspective-d8bdd828.html|website=www.mexicohistorico.com|accessdate=2025-11-02}}{{Dead link|date=August 2026 |bot=InternetArchiveBot |fix-attempted=yes }}</ref> '''ಸೋವಿಯತ್ ಸಹಾಯ''': ಶೀತಯುದ್ಧದ ಕಾಲದಲ್ಲಿ, ಕ್ಯೂಬಾ $33 ಬಿಲಿಯನ್ ಸೋವಿಯತ್ ಸಹಾಯ ಪಡ್ದು. ಇತ್ತೆ ಆರ್ಥಿಕ ಬಲವರ್ಧನೆ, ಸೇನಾ ಸಾಮರ್ಥ್ಯ, ಬುಕ್ಕೊಾ ರಾಜಕೀಯ ಸ್ಥಿರತೆಗಾಗಿ ಉಪಯೋಗ ಆಯಿದು<ref>{{Cite journal|title=U.S.-Cuban Relations: From the Cold War to the Colder War|url=https://www.jstor.org/stable/166485|journal=Journal of Interamerican Studies and World Affairs|date=1997|issn=0022-1937|pages=49–75|volume=39|issue=3|doi=10.2307/166485|first=Jorge I.|last=Domínguez}}</ref>. == ಆರ್ಥಿಕತೆ == ಕ್ಯೂಬಾ ಒಂಜಿ ಯೋಜಿತ ಆರ್ಥಿಕ ವ್ಯವಸ್ಥೆ ಇತ್ತೆ, ಜಾಸ್ತಿ ಉದ್ಯಮಗಳು ಸರ್ಕಾರದ ನಿಯಂತ್ರಣದ ತಡೆಯಲ್ಲಿ ಇತ್ತೆ. ಪ್ರವಾಸೋದ್ಯಮ, ಸಕ್ಕರೆ, ತಂಬಾಕು, ಕಾಫಿ, ಬುಕ್ಕೊಾ ವೈದ್ಯಕೀಯ ಸೇವೆ ಇತ್ತೆ ದೇಶದ ಪ್ರಮುಖ ಆದಾಯದ ಮೂಲ. 2010 ಇಂದ ಸ್ವಲ್ಪ ಖಾಸಗಿ ಉದ್ಯಮಕ್ಕೆ ಅವಕಾಶ ಕೊಟ್ಟ್‌ಡು, ಆರ್ಥಿಕ ಸುಧಾರಣೆಗೊಲು. 1960 ಇಂದ ಇಪ್ಪುನ ತನಕ ಅಮೆರಿಕದ ನಿರ್ಬಂಧ ನಡಿತಾ ಇತ್ತೆ, ಇತ್ತೆ ವ್ಯಾಪಾರ ಬುಕ್ಕೊಾ ಹಣಕಾಸು ವ್ಯವಹಾರ ತಡೆ ಇತ್ತೆ. 2021 ತಡೆ, ಎರಡು ಕರೆನ್ಸಿ ವ್ಯವಸ್ಥೆ ಒಟ್ಟುಗೂಡ್ದು, ದರ ಏರಿಕೆ ಬುಕ್ಕೊಾ ಅಸ್ಥಿರತೆ ಉಂಟಾಯಿದು. 2023 OCDH ವರದಿಯ ತಡೆ, 88% ಜನರು ತೀವ್ರ ಬಡತನದ ತಡೆ ಬಾಳ್ತಾ ಇತ್ತೆ, ಇತ್ತೆ ಆರ್ಥಿಕ ಸಂಕಷ್ಟದ ತೀವ್ರತೆ ತೋರ್ದು. == భూగోళ శాస్త్రం == ಕುಬಾ ೧೯°N ಮುಕ್ಕಲ್ನು ೨೪°N ಮುಕ್ಕಲು ಅಂಟು ೭೪°W ಮುಕ್ಕಲು ೮೫°W ಮುಕ್ಕಲು ಲಾಂಗಿಟ್ಯೂಡ್ಸ್ ನಲ್ಲಿ ಇರ್ನ್ನು ಉತ್ತರ ಕ್ಯಾರಿಬಿಯನ್ ಕಡಲ್ನುಲಿ, ಮೆಕ್ಸಿಕೋ ಗಲ್ಫ್ ಮತ್ತ ಅಟ್ಲಾಂಟಿಕ್ ಓಷನ್ ಸಂಧಾನದ ಜಾಗದಲ್ಲಿ 4,195 ದ್ವೀಪ, ಕಸ್ ಆನ್ ತುದಿ ದ್ವೀಪಗಳ ಸುಂದರ ಜೋಡಣೆಯಾಗಿದೆ. ಫ್ಲೋರಿಡಾ (ಕಿ ವೆಸ್ಟ್, ಫ್ಲೋರಿಡಾ) ಉತ್ತರ ಉ ಉತ್ತರ-ಪಶ್ಚಿಮ ದಿಕ್ಕು 150 ಕಿಮೀ (93 ಮೈಲಿ) ದೂರವನು ಫ್ಲೋರಿಡಾ ಸ್ಟ್ರೇಟ್ಸ್ ದಾಟಿ ಇರುತ್ತದೆ, ಮತ್ತ ದ ಬಾಹಾಮಾಸ್ (ಕೇ ಲೊಬೋಸ್) 22.5 ಕಿಮೀ (14 ಮೈಲಿ) ಉತ್ತರ ದಿಕ್ಕು ಇರುತ್ತದೆ. ಮೆಕ್ಸಿಕೋ ಯುಕ್ಯಾಟಾನ್ ಚಾನಲ್ ದಾಟಿ ಪಶ್ಚಿಮ ದಿಕ್ಕು 210 ಕಿಮೀ (130.5 ಮೈಲಿ), ಕುಯಿಂಟಾನಾ ರೂ ರಾಜ್ಯದ ಕಬೋ ಕ್ಯಾಟೋಚೆ ತುದಿಯೊಂದಿಗೆ ಅಲ್ಪದು ದೂರವು ಇರುತ್ತದೆ<ref>{{Cite web|title=Where is Cuba in the World?|url=https://worldpopulationreview.com/countries/cuba/location|website=World Population Review|date=2025-10-28|accessdate=2025-11-02|language=en}}</ref>. హైటి క్యూబాకు తూర్పు దిశగా సుమారు 78 కి.మీ (48.5 మైళ్లు) దూరంలో ఉంది, మరియు జమైకా దక్షిణ దిశగా 148 కి.మీ (92 మైళ్లు) దూరంలో ఉంది. క్యూబా ప్రధాన ద్వీపం కాగా, దాని చుట్టూ నాలుగు చిన్న దీవుల సమూహాలు ఉన్నాయి: ఉత్తర–పశ్చిమ తీరంలో కలరాడోస్ ఆర్చిపెలాగో, ఉత్తర–మధ్య అట్లాంటిక్ తీరంలో సబానా–కమాగువే ఆర్చిపెలాగో, దక్షిణ–మధ్య తీరంలో జార్డినెస్ డే లా రీనా, మరియు దక్షిణ–పశ్చిమ తీరంలో కానారియోస్ ఆర్చిపెలాగో<ref name=":2">{{Cite web|title=Cuba - The World Factbook|url=https://www.cia.gov/the-world-factbook/about/archives/2023/countries/cuba/|website=www.cia.gov|accessdate=2025-11-02|archivedate=2026-01-15|archive-url=https://web.archive.org/web/20260115123700/https://www.cia.gov/the-world-factbook/about/archives/2023/countries/cuba/|url-status=dead}}</ref><ref name=":3">{{Cite web|title=Cuba {{!}} Government, Flag, Capital, Population, & Language {{!}} Britannica|url=https://www.britannica.com/place/Cuba|website=www.britannica.com|date=2025-11-02|accessdate=2025-11-02|language=en}}</ref>. ప్రధాన ద్వీపం క్యూబా అని పిలవబడుతుంది. ఇది 1,250 కి.మీ (780 మైళ్లు) పొడవుగా ఉండి, దేశ భూభాగంలో ఎక్కువ భాగాన్ని — అంటే 104,338 చ.కి.మీ (40,285 చ.మైళ్లు) — ఆక్రమించి ఉంది. ఇది కరేబియన్ ప్రాంతంలోని అత్యంత పెద్ద ద్వీపం మాత్రమే కాకుండా, భూభాగ పరిమాణం ప్రకారం ప్రపంచంలో 17వ అతిపెద్ద ద్వీపం కూడా. ప్రధాన ద్వీపం ఎక్కువగా సమతల ప్రాంతాలు లేదా కొద్దిగా ఎత్తు తక్కువ గల మైదానాలతో కూడి ఉంటుంది, అయితే ఆగ్నేయ దిశలో ఉన్న సియెరా మాయెస్ట్రా పర్వతాలు ఈ భూభాగానికి భిన్నంగా ఉంటాయి. ఆ పర్వతాల్లో ఉన్న పికో టుర్కినో అనే శిఖరం 1,974 మీటర్లు (6,476 అడుగులు) ఎత్తు కలిగి ఉంది<ref name=":3" />. రెండవ అతిపెద్ద ద్వీపం కానారియోస్ దీవుల సమూహంలో ఉన్న ఇస్లా డే లా జువెంటుడ్ (యూత్ ద్వీపం) కాగా, దాని విస్తీర్ణం 2,204 చ.కి.మీ (851 చ.మైళ్లు). క్యూబా యొక్క అధికారిక విస్తీర్ణం **109,884 చ.కి.మీ (42,426 చ.మైళ్లు)**గా పేర్కొనబడింది. అయితే CIA ప్రకారం, దాని విస్తీర్ణం **110,860 చ.కి.మీ (42,803 చ.మైళ్లు)**గా ఉంటుంది<ref name=":3" />. ట్రాపిక్ ఆఫ్ క్యాన్సర్‌కు దక్షిణంగా మొత్తం ద్వీపం ఉండటంతో, స్థానిక వాతావరణం ఉష్ణమండల వాతావరణంగా ఉంటుంది. అయితే సంవత్సరం పొడవునా వీచే ఈశాన్య దిశ గాలులు (ట్రేడ్ విండ్స్) దానిని కొంత మితంగా ఉంచుతాయి. సముద్ర ఉష్ణోగ్రతను కరేబియన్ ప్రవాహం ప్రభావితం చేస్తుంది, ఇది భూమధ్యరేఖ ప్రాంతం నుంచి వేడి నీటిని తీసుకువస్తుంది. దీనివల్ల క్యూబా వాతావరణం హాంకాంగ్ కంటే వేడిగా ఉంటుంది — రెండూ దాదాపు ఒకే అక్షాంశంలో ఉన్నప్పటికీ, హాంకాంగ్‌కు ఉపఉష్ణమండల వాతావరణం ఉండగా, క్యూబాకు నిజమైన ఉష్ణమండల వాతావరణం ఉంటుంది. సాధారణంగా (ప్రాంతాలవారీగా మారవచ్చు) నవంబర్‌ నుండి ఏప్రిల్‌ వరకు పొడి కాలం, మే నుండి అక్టోబర్‌ వరకు వర్షాకాలం ఉంటుంది. జనవరిలో సగటు ఉష్ణోగ్రత 21&nbsp;°C (70&nbsp;°F), జూలైలో 27&nbsp;°C (81&nbsp;°F) ఉంటుంది. కరేబియన్ సముద్రం వేడి నీటి ప్రభావం మరియు క్యూబా మెక్సికో గల్ఫ్ ప్రవేశద్వారం వద్ద ఉన్న భౌగోళిక స్థానం కలిసివచ్చి, ఈ దేశాన్ని తరచూ చండమారుతాల (హరికేన్ల) ప్రభావానికి గురిచేస్తాయి. ఇవి ఎక్కువగా సెప్టెంబర్‌ మరియు అక్టోబర్‌ నెలల్లో సంభవిస్తాయి.<ref name=":2" /><ref>{{Cite web|title=Home Page|url=https://courses.washington.edu/tesc243/cuba/Section%20five%20climate.shtml?utm_source=chatgpt.com|website=courses.washington.edu|accessdate=2025-11-02}}</ref> ಕ್ಯೂಬಾದಲ್ಲಿ ಹವಾಮಾನ ಬದಲಾವಣೆ ಉಷ್ಣತೆ ಹೆಚ್ಚಳ, ಕಡಲಿನ ಮಟ್ಟದ ಏರಿಕೆ ಮತ್ತು ಮಳೆಯ ಮಾದರಿಗಳ ಬದಲಾವಣೆಯನ್ನುಂಟುಮಾಡುತ್ತಿದೆ, ಇದು ಕೃಷಿ, ಅರಣ್ಯವಲಯ ಮತ್ತು ಪ್ರವಾಸೋದ್ಯಮ ಸೇರಿದಂತೆ ಪ್ರಮುಖ ಆರ್ಥಿಕ ಕ್ಷೇತ್ರಗಳನ್ನು ಗಟ್ಟಿಯಾಗಿ ಪ್ರಭಾವಿಸುತ್ತಿದೆ<ref>{{Cite web|title=Cuba|url=https://www.undp.org/es/cuba|website=UNDP|accessdate=2025-11-02|archiveurl=https://web.archive.org/web/20250720130946/https://www.undp.org/es/cuba|archivedate=2025-07-20|language=es|url-status=live}}</ref>. ಕ್ಯೂಬಾದಲ್ಲಿ ಹವಾಮಾನ ಬದಲಾವಣೆಯ ಪರಿಣಾಮವಾಗಿ ತಾಪಮಾನ ಹೆಚ್ಚಲಿದೆ, ಕಡಲು ಮಟ್ಟಗಳು ಏರಿಕೆಗೊಳ್ಳುತ್ತವೆ ಮತ್ತು ಮಳೆಯ ಮಾದರಿಗಳು ಗೆಾಳಿ ಗೊಂಭಳಿಕವಾಗುತ್ತವೆ. ವಿಶೇಷವಾಗಿ, ಮಳೆಯ ಒಟ್ಟು ಪ್ರಮಾಣ ಕೆಳಮಟ್ಟಕ್ಕೆ ಇಳಿಯುವ ಸಾಧ್ಯತೆ ಇದೆ, ಇದು ಉಳಿತಾಯ, ನೀರಿನ ಲಭ್ಯತೆ ಮತ್ತು ಕೃಷಿ ಉತ್ಪಾದನೆಗೆ ದೊಡ್ಡ ಹಿಂಜೆಗಾಗುತ್ತದೆ. ಕ್ಯೂಬಾದಲ್ಲಿ ಮಳೆ ಯಾವುದು ಒಬ್ಬರೂ ಸಾಮಾನ್ಯ ಜಲಸಂಪತ್ತಾಗಿದೆ, ಆದ್ದರಿಂದ ಜಲ ಸುರಕ್ಷತೆ ಪ್ರಮುಖ ಸಮಸ್ಯೆಯಾದೀತು.<ref>{{Cite web|title=Cuba Case Study {{!}} Climate Refugees {{!}} Othering & Belonging Institute|url=https://belonging.berkeley.edu/climatedisplacement/case-studies/cuba?utm_source=chatgpt.com|website=belonging.berkeley.edu|accessdate=2025-11-02|language=en}}</ref> ಕೃಷಿ ಮತ್ತು ಅರಣ್ಯವು ಬದಲಾವಣೆಯ ಪರಿಣಾಮವಾಗಿ ನಷ್ಟಕ್ಕೆ ಒಳಪಡಬಹುದು, ಏಕೆಂದರೆ ಬೆಳೆಗಳ ಬೆಳವಣಿಗೆಗೆ ಬೇಕಾದ ಮಾರುಕಟ್ಟೆ ನೀರು ಕೊರತೆ ಆಗಬಹುದು. ಪ್ರವಾಸೋದ್ಯಮವೂ ಹಾನಿಯಾಗಬಹುದು, ವಿಶೇಷವಾಗಿ ಕಡಲು ತೀರ ಪ್ರದೇಶಗಳಲ್ಲಿ ಬಡಾವಣೆಯು ಕಡಲಿನ ನೀರಿನ ಏರಿಕೆಯೊಂದಿಗೆ ದೃಷ್ಟಿಯಲ್ಲಿ ಬರುತ್ತದೆ<ref name=":4">{{Cite web|title=Small Island Developing States are on the frontlines of climate change – here's why {{!}} UNDP Climate Promise|url=https://climatepromise.undp.org/news-and-stories/small-island-developing-states-are-frontlines-climate-change-heres-why|website=climatepromise.undp.org|date=2025-09-23|accessdate=2025-11-02|language=en}}</ref>. ಆರೋಗ್ಯದ ಮೇಲೆ ಪರಿಣಾಮಗಳು ಸಹ ಬಾಧಕವಾಗಬಹುದು. ವಾತಾವರಣದ ಉಷ್ಣತೆ ಹೆಚ್ಚಿದಂತೆ ಹೃದಯ ಸಂಬಂಧಿತ, ಶ್ವಾಸಕೋಶ ಹಾಗೂ ವೈರಲ್ ರೋಗಗಳ ಪ್ರಮಾಣ ಹೆಚ್ಚುವ ಸಂಭವವಿದೆ, ಮತ್ತು ಬಯಾಲಾಜಿಕಲ್ ಸೋರ್ಷಕಗಳು ಮತ್ತು ಕೀಟಗಳು ಹೆಚ್ಚು ಸಕ್ರಿಯವಾಗಬಹುದು<ref>{{Cite web|title=Climate change|url=https://www.who.int/news-room/fact-sheets/detail/climate-change-and-health|website=www.who.int|accessdate=2025-11-02|language=en}}</ref><ref>{{Cite web|title=The Caribbean Public Health Agency (CARPHA)|url=https://carpha.org/|website=www.carpha.org|accessdate=2025-11-02|archiveurl=https://web.archive.org/web/20250928211934/https://carpha.org/|archivedate=2025-09-28|language=en-US|url-status=live}}</ref>. ಹೀಗಾಗಿ, ಕ್ಯೂಬಾದದಲ್ಲಿ ಹವಾಮಾನ ಬದಲಾವಣೆ ಜಲ, ಆಹಾರ, ಉದ್ಯಮ ಮತ್ತು ಆರೋಗ್ಯ ಎಲ್ಲೆಡೆಯಲ್ಲಿಯೂ ಪ್ರಮುಖ ತೊಂದರೆಗಳನ್ನುಂಟುಮಾಡಬಹುದು. ಜಲ ನಿರ್ವಹಣೆ, ಕೃಷಿ ತಂತ್ರಜ್ಞಾನ ಅಭಿವೃದ್ಧಿ ಮತ್ತು ಸಾರ್ವಜನಿಕ ಆರೋಗ್ಯ ತಂತ್ರಗಳನ್ನು ಬಳಸಿದ ಸುಧಾರಿತ ತಂತ್ರಗಳು, ಹವಾಮಾನ ಬದಲಾವಣೆಯ ಪರಿಣಾಮವನ್ನು ಕಡಿಮೆ ಮಾಡುವಲ್ಲಿ ಸಹಾಯ ಮಾಡಬಹುದು.<ref>{{Cite web|title=CubaCUB - Country Overview {{!}} Climate Change Knowledge Portal|url=https://climateknowledgeportal.worldbank.org/country/cuba|website=climateknowledgeportal.worldbank.org|accessdate=2025-11-02|language=en}}</ref>. పరిశ్రమల పూర్వస్థాయిలతో పోలిస్తే ఉష్ణోగ్రత 2&nbsp;°C మేర పెరిగితే, క్యూబాలో తీవ్రమైన తుఫాన్ వర్షపాతం సంభవించే అవకాశం మూడు రెట్లు పెరుగుతుంది.<ref>{{Cite web|title=Climate change may make extreme hurricane rainfall 5 times more likely, study says - CBS News|url=https://www.cbsnews.com/news/climate-change-may-make-extreme-hurricane-rainfall-five-times-more-likely-study-says/|website=www.cbsnews.com|date=2020-08-29|accessdate=2025-11-02|language=en-US}}</ref>. క్యూబా యొక్క వాతావరణ మార్పు నియంత్రణ మరియు అనుకూలత ప్రణాళికల్లో పునరుత్పాదక శక్తి ఉత్పత్తితో పాటు మ్యాంగ్రోవ్ పర్యావరణ వ్యవస్థలను పునరుద్ధరించడం వంటి ప్రకృతి ఆధారిత పరిష్కారాలు కూడా ఉన్నాయి.<ref name=":4" />. హరికేన్ ఇర్మా 2017 సెప్టెంబర్ 8న క్యూబా ద్వీపాన్ని తాకింది, ఆ సమయంలో గాలుల వేగం **260 కి.మీ/గం (72 మీ/సెకం)**గా నమోదైంది.<ref>{{Cite web|title=Florida braces for ‘life threatening’ Hurricane Irma|url=https://www.aljazeera.com/news/2017/9/10/florida-braces-for-hurricane-irma-after-cuba-landfall|website=Al Jazeera|accessdate=2025-11-02|language=en}}</ref>, కమాగువే ఆర్చిపెలాగోలో తుఫాన్ అగ్రసరించగా, మధ్యరాత్రి సమయానికి ఇది సియేగో డే అవిలా ప్రావిన్స్ చేరింది మరియు తదుపరి రోజున క్యూబా మీద దాడి కొనసాగించింది.<ref>{{Cite news|url=https://beta.theglobeandmail.com/news/world/hurricane-irma-rips-through-cuba-on-its-way-to-florida/article36221350/|title=Winds whip Florida Keys as Hurricane Irma turns sights northward|date=2017-09-09|work=The Globe and Mail|access-date=2025-11-02|language=en-GB|archive-date=2017-09-10|archive-url=https://web.archive.org/web/20170910053409/https://beta.theglobeandmail.com/news/world/hurricane-irma-rips-through-cuba-on-its-way-to-florida/article36221350/|url-status=dead}}</ref>. క్యూబా ప్రధాన ద్వీపం ఉత్తరంలోని కీస్‌ ప్రాంతంలో తీవ్ర నష్టాలు సంభవించాయి. ఆసుపత్రులు, గودాములు మరియు ఫ్యాక్టరీలు దెబ్బతిన్నాయి; ఉత్తర తీరప్రాంతంలో పెద్ద భాగానికి విద్యుత్ అందలేదు. ఆ సమయంలో, సుమారు ఒక మిలియన్ మందికి పైగా ప్రజలు, విపరీత పరిస్థితుల వల్ల, పర్యాటకులు సహా эвాక్యుయేట్ చేయబడ్డారు.<ref>{{Cite news|url=https://www.nytimes.com/2017/09/09/us/hurricane-irma-florida.html|title=Storm Gains Strength as It Nears Florida (Published 2017)|date=2017-09-09|access-date=2025-11-02|language=en}}</ref>. వారాడెరొ రిసార్ట్ ప్రాంతంలో కూడా విస్తృత నష్టం నమోదయింది; ప్రభుత్వం ప్రధాన పర్యాటక సీజన్ ప్రారంభానికి ముందు మురాములు పూర్తవుతాయని విశ్వసించింది.<ref name=":5">{{Cite web|title=Irma kills 10 people in Cuba|url=https://www.cnn.com/2017/09/11/americas/irma-cuba|website=CNN|date=2017-09-11|accessdate=2025-11-02|language=en|first=Hilary Clarke,Patrick|last=Oppmann}}</ref>. తదుపరి నివేదికల ప్రకారం, తుఫాన్ సమయంలో పది మంది ప్రాణాలు కోల్పోయారు, అందులో హవానాలో ఏడు మంది ఉన్నారు, వీరిలో ఎక్కువరు భవనాలు పడిపోవడంవల్ల మరణించారు. రాజధాని నగరంలోని కొన్ని ప్రాంతాలు నీటిలో మునిగిపోయాయి<ref name=":5" />. === జీవవివిధత్వం === క్యూబా బయోలాజికల్ డైవర్సిటీపై రియో కాన్వెన్షన్ను 1992 జూన్ 12న సంతకం చేసింది, మరియు 1994 మార్చ్ 8న ఈ కాన్వెన్షన్‌లో పార్టీ అయ్యింది. తరువాత, క్యూబా **జాతీయ జీవవైవిధ్య వ్యూహం మరియు చర్యల ప్రణాళిక (National Biodiversity Strategy and Action Plan)**ను రూపొందించింది, ఇందులో ఒక సవరణ చేయబడింది, మరియు ఈ సవరణ 2008 జనవరి 24న కాన్వెన్షన్‌కు అందజేయబడింది<ref name=":6">{{Cite web|title=Main Details|url=https://www.cbd.int/countries/profile/default.shtml?country=cu|website=www.cbd.int|accessdate=2025-11-02|language=en|first=Biosafety|last=Unit}}</ref>. క్యూబా యొక్క CBD (Convention on Biological Diversity) కి ఇచ్చిన నాల్గవ జాతీయ నివేదిక క్యూబాలో నమోదైన జీవరాజ్యాల జాతుల సంఖ్యను విస్తృతంగా వివరించుతుంది. ప్రధాన జాతి గుంపులు ఇవి<ref name=":6" />: * జంతువులు: 17,801 జాతులు<ref name=":6" /> * బాక్టీరియా: 270 జాతులు<ref name=":6" /> * క్రొమిస్టా: 707 జాతులు<ref name=":6" /> * ఫంగస్ (లైకెన్-ఫార్మింగ్ జాతులు సహా): 5,844 జాతులు<ref name=":6" /> * సస్యాలు: 9,107 జాతులు<ref name=":6" /> * ప్రోటోజోవా: 1,440 జాతులు<ref name=":6" /> స్థానిక ప్రాణులు: * బీ హమ్మింగ్‌బర్డ్ లేదా జుంజున్సిటో: ప్రపంచంలో తెలిసిన చిన్న తీరుపు పక్షి, పొడవు 55 మి.మీ (2+1/8 ఇంచులు). * క్యూబన్ ట్రోగన్ లేదా టోకోరోరో: క్యూబా జాతీయ పక్షి, మరియు స్థానిక/endemic జాతి. ఇతర స్థానిక/endemic జాతులు: * క్యూబన్ ముక్కరైన మొసలి (Cuban crocodile) * క్యూబన్ హుటియా (Cuban hutia) * క్యూబన్ సోలెనోడాన్ (Cuban solenodon) * క్యూబన్ గార్ (Cuban gar) * క్యూబన్ బోవా (Cuban boa) * పోలిమిటా పిక్టా (Polymita picta) జాతీయ పువ్వు: హెడిచియం కోరోనేరియం (Hedychium coronarium), క్యూబాలో దీనిని మారిపోసా అని పిలుస్తారు.<ref>{{Cite book|url=https://books.google.com/books?id=yzIlAZ6OPsUC&pg=PA69|title=Historical Dictionary of Cuba|last=Suchlicki|first=Jaime|date=2001|publisher=Scarecrow Press|isbn=978-0-8108-2071-5|language=en}}</ref> క్యూబాలో మొత్తం ఆరు భూస్థల ఎకోరీజన్లు (terrestrial ecoregions) ఉన్నాయి. వీటిలో క్యూబన్ తేమగల అడవులు, క్యూబన్ పొడి అడవులు, క్యూబన్ పొగాకు అడవులు, క్యూబన్ తోటలు లేదా బాగ్ ప్రాంతాలు, క్యూబన్ సక్కర చెట్లు/కాక్టస్ స్క్రబ్, మరియు గ్రేటర్ అంటిల్స్ మ్యాంగ్రోవ్స్ ఉన్నాయి. ఈ విభిన్న ఎకోరీజన్లు క్యూబా యొక్క వైవిధ్యభరితమైన భూభాగం మరియు జీవవైవిధ్యానికి కీలకమైన పాత్ర పోషిస్తున్నాయి.<ref>{{Cite journal|title=An Ecoregion-Based Approach to Protecting Half the Terrestrial Realm|url=https://pmc.ncbi.nlm.nih.gov/articles/PMC5451287/|journal=Bioscience|date=2017-06-01|issn=0006-3568|pmc=5451287|pmid=28608869|pages=534–545|volume=67|issue=6|doi=10.1093/biosci/bix014|first=Eric|last=Dinerstein|first2=David|last2=Olson|first3=Anup|last3=Joshi|first4=Carly|last4=Vynne|first5=Neil D.|last5=Burgess|first6=Eric|last6=Wikramanayake|first7=Nathan|last7=Hahn|first8=Suzanne|last8=Palminteri|first9=Prashant|last9=Hedao|first10=Reed|last10=Noss|first11=Matt|last11=Hansen|first12=Harvey|last12=Locke|first13=Erle C.|last13=Ellis|first14=Benjamin|last14=Jones|first15=Charles Victor|last15=Barber}}</ref>. 2019లో, క్యూబా ఫారెస్ట్ ల్యాండ్‌స్కేప్ ఇన్టిగ్రిటీ ఇండెక్స్లో 5.4/10 సగటు స్కోరు పొందింది, ప్రపంచవ్యాప్తంగా 172 దేశాలలో 102వ స్థానంను ర్యాంక్ చేసింది.<ref>{{Cite journal|title=Anthropogenic modification of forests means only 40% of remaining forests have high ecosystem integrity|url=https://pmc.ncbi.nlm.nih.gov/articles/PMC7723057/|journal=Nature Communications|date=2020-12-08|issn=2041-1723|pmc=7723057|pmid=33293507|pages=5978|volume=11|issue=1|doi=10.1038/s41467-020-19493-3|first=H. S.|last=Grantham|first2=A.|last2=Duncan|first3=T. D.|last3=Evans|first4=K. R.|last4=Jones|first5=H. L.|last5=Beyer|first6=R.|last6=Schuster|first7=J.|last7=Walston|first8=J. C.|last8=Ray|first9=J. G.|last9=Robinson|first10=M.|last10=Callow|first11=T.|last11=Clements|first12=H. M.|last12=Costa|first13=A.|last13=DeGemmis|first14=P. R.|last14=Elsen|first15=J.|last15=Ervin}}</ref>. == Government and politics == ಕುಬಾ ಗಣರಾಜ್ಯ ಒನ್‑ಪಾರ್ಟಿ ಸೋಶಿಯಲಿಸ್ಟ್ ರಾಜ್ಯ ಆಣ್, ಜೆರ್ಮ್ ಮಾರ್ಕ್ಸಿಸಮ್‑ಲೆನಿನಿಸಮ್ ಆಳೋಚನೆಯು ಅನುಸರಿಸುತ್ತದೆ. 1976 ರ ಸಂವಿಧಾನ, ಜೆಡ್ಕುಬಾ ಸೋಶಿಯಲಿಸ್ಟ್ ಗಣರಾಜ್ಯ ಎಣ್ಣೆ ಪರಿಗಣಿಸಿತು, 1992 ರ ಸಂವಿಧಾನ ದ್ವಾರಾ ಬದಲಾಯಿಸಲ್ಪಟ್ಟು, ಇದ್ “ಹೊಸೆ ಮಾರ್ಟಿ ಆಣ್ ಮಾರ್ಕ್ಸ್, ಎಂಗೆಲ್ಸ್ ಆಣ್ ಲೆನಿನ್ ರ ರಾಜಕೀಯ ಆಣ್ ಸಾಮಾಜಿಕ ಆಳೋಚನೆಗಳ ಮಾರ್ಗದರ್ಶನದ ಅಡಿಯಲ್ಲಿ” ಇದ್ದು.<ref name=":7">{{Cite web|title=Wayback Machine|url=http://www.constitutionnet.org/files/Cuba%20Constitution.pdf|website=www.constitutionnet.org|accessdate=2025-11-02|archiveurl=https://web.archive.org/web/20130117013359/http://www.constitutionnet.org/files/Cuba%20Constitution.pdf|archivedate=2013-01-17}}</ref> ಸಂವಿಧಾನದಲ್ಲಿ ಕುಬಾ ಕಮ್ಯುನಿಸ್ಟ್ ಪಕ್ಷನ್ನು “ಸಮಾಜ ಆಣ್ ರಾಜ್ಯದ ಮುಖ್ಯ ನಾಯಕರ ಶಕ್ತಿ” ಎಂದು ವರ್ಣಿಸಲಾಗಿದೆ.<ref name=":7" />. ಕುಬಾದ ರಾಜಕೀಯ ವ್ಯವಸ್ಥೆ ಮಾರ್ಕ್ಸ್‑ಲೆನಿನ್ ಆಳೋಚನೆಯ ಪ್ರಜಾಪ್ರಭುತ್ವ ಕೇಂದ್ರತಂತ್ರ (Democratic Centralism) ಪರಿಕಲ್ಪನೆಯನ್ನು ಪ್ರತಿಬಿಂಬಿಸುತ್ತದೆ.<ref>{{Cite book|title=The Low-Carbon Contradiction: Energy Transition, Geopolitics, and the Infrastructural State in Cuba|last=Cederlof|first=Gustav|date=2023|publisher=University of California Press|isbn=978-0-520-39313-4|edition=1st ed|series=Critical Environments: Nature, Science, and Politics Series|location=Berkeley}}</ref>. ಕುಬಾ ಕಮ್ಯುನಿಸ್ಟ್ ಪಕ್ಷದ ಮுதல் ಕಾರ್ಯದರ್ಶಿ ಒನ್‑ಪಾರ್ಟಿ ರಾಜ್ಯದ ಅತ್ಯಂತ ಹಿರಿಯ ಸ್ಥಾನವಾಗಿದೆ. ಮೊದಲ ಕಾರ್ಯದರ್ಶಿ ಪೊಲಿಟ್‌ಬ್ಯುರೋ ಮತ್ತು ಸೆಕ್ರೆಟರಿಯಟ್ ಅನ್ನು ಮುನ್ನಡೆಸುತ್ತಾನೆ, ಇದರಿಂದ ಈ ಸ್ಥಾನದ ವಹಿಸುವವರು ಕುಬಾದ ಸರ್ಕಾರದಲ್ಲಿ ಅತ್ಯಂತ ಶಕ್ತಿಶಾಲಿ ವ್ಯಕ್ತಿಯಾಗುತ್ತಾರೆ.<ref>{{Cite news|url=http://news.bbc.co.uk/2/hi/americas/country_profiles/1203299.stm|title=Country profile: Cuba|date=2012-08-14|access-date=2025-11-02|language=en-GB}}</ref>.ಎರಡು ಮಂಡಳಿಗಳ ಸದಸ್ಯರನ್ನೂ ಜನಶಕ್ತಿ ರಾಷ್ಟ್ರೀಯ ಸಭೆ ಆಯ್ಕೆಮಾಡುತ್ತದೆ.<ref name=":7" />. ಕುಬಾದ ಅಧ್ಯಕ್ಷರು, ಜೆಡ್ಕು ಸಭೆ ಆಯ್ಕೆಮಾಡುತ್ತೆ, ಐದು ವರ್ಷಗಳ ಕಾಲ ಸೇವೆ ಮಾಡ್ತಾರೆ, ಆಣ್ 2019 ರ ಸಂವಿಧಾನ ಅನುಮೋದನೆಯಿಂದ ಎರಡು ಐದು ವರ್ಷದ ಅವಧಿಗಳ ಮಿತಿ ಇದ್ದು..<ref name=":7" /> ಜನರ ಸುಪ್ರೀಂ ಕೋರ್ಟ್ ಕುಬಾದ ಅತ್ಯಂತ ಉನ್ನತ ನ್ಯಾಯಾಂಗ ಶಾಖೆ ಆಗಿದೆ. ಇದು ಪ್ರಾಂತ್ಯೀಯ ನ್ಯಾಯಾಲಯಗಳ ನಿರ್ಣಯಗಳ ವಿರುದ್ಧ ಎಲ್ಲಾ ಹಿತಾಸಕ್ತಿ ಹಕ್ಕುಗಳ ಕೊನೆಯ ನಿಲುವಿನ ನ್ಯಾಯಾಲಯ ಆಗಿಯೂ ಸೇವೆ ನೀಡುತ್ತದೆ. ಕುಬಾದ ರಾಷ್ಟ್ರೀಯ ವಿಧಾನಸಭೆ, ಅಂದರೆ ಜನಶಕ್ತಿ ರಾಷ್ಟ್ರೀಯ ಸಭೆ (Asamblea Nacional de Poder Popular), ಅತ್ಯಂತ ಶಕ್ತಿ ಸಂಸ್ಥೆ ಆಗಿದೆ; 474 ಸದಸ್ಯರು ಐದು ವರ್ಷದ ಅವಧಿಗೆ ಸೇವೆ ಸಲ್ಲಿಸುತ್ತಾರೆ.<ref name=":7" />. ಸಭೆ ವರ್ಷಕ್ಕೆ ಎರಡು ಸಲ ಸೇರ್ಪಡೆಗೊಳ್ಳುತ್ತದೆ; ಸತ್ರಗಳ ನಡುವೆ ವಿಧಾನ ಶಕ್ತಿ 31 ಸದಸ್ಯರ ಮಂತ್ರಿಗಳ ಮಂಡಳಿ ಯಲ್ಲಿ ಇರುತ್ತದೆ. ಸಭೆಗೆ ಅಭ್ಯರ್ಥಿಗಳನ್ನು ಸಾರ್ವಜನಿಕ ಜನಮತದ್ವಾರಾ ಅನುಮೋದಿಸಲಾಗುತ್ತದೆ. ಕ್ರಿಮಿನಲ್ ಅಪರಾಧಕ್ಕೆ ದೋಷಾರೋಪಿಸ್ಪಡದ 16 ವರ್ಷಕ್ಕೂ ಮೇಲ್ಪಟ್ಟ ಎಲ್ಲಾ ಕುಬಾದ ನಾಗರಿಕರು ಮತ ಹಾರಿಸಬಹುದು.<ref>{{Cite web|title=Cuba 1976 (rev. 2002) Constitution - Constitute|url=https://www.constituteproject.org/constitution/Cuba_2002|website=www.constituteproject.org|accessdate=2025-11-02|language=en}}</ref>. ಸಂವಿಧಾನದ ಲೇಖ 131 ಹೇಳುತ್ತದೆ, ಮತದಾನವು "ಸ್ವತಂತ್ರ, ಸಮಾನ ಮತ್ತು ಗುಪ್ತ ಮತದಾನ" ಆಗಿರಬೇಕು ಎಂದು.ಲೇಖ 136 ಹೇಳುತ್ತದೆ: "ಸದಸ್ಯರು ಅಥವಾ ಪ್ರತಿನಿಧಿಗಳು ಆಯ್ಕೆಯಾಗಿದ್ದರೆ, ಚುನಾವಣೆ ಜಿಲ್ಲೆಗಳಲ್ಲಿ ಹಾಕಲ್ಪಟ್ಟ ಮಾನ್ಯ ಮತಗಳ ಅರ್ಧಕ್ಕಿಂತ ಹೆಚ್ಚು ಮತಗಳನ್ನು ಪಡೆದಿರಬೇಕು".<ref name=":7" /> ಕುಬಾದಲ್ಲಿ ಚುನಾವಣೆಗಳಿವೆ, ಆದರೆ ಅವು ಪ್ರಜಾಪ್ರಭುತ್ವ ಪ್ರಕಾರದಂತೆ ಪರಿಗಣಿಸಲ್ಪಡುವುದಿಲ್ಲ.<ref>{{Cite journal|title=Who wins the most when everybody wins? Predicting candidate performance in an authoritarian election|url=https://www.tandfonline.com/doi/full/10.1080/13510347.2019.1629420|journal=Democratization|date=2019-10-03|issn=1351-0347|pages=1278–1298|volume=26|issue=7|doi=10.1080/13510347.2019.1629420|language=en|first=Ángela|last=Fonseca Galvis|first2=Chiara|last2=Superti}}</ref><ref>{{Cite journal|title=Authoritarian Regimes and Their Permitted Oppositions: Election Day Outcomes in Cuba|url=https://www.cambridge.org/core/product/identifier/S1531426X00001515/type/journal_article|journal=Latin American Politics and Society|date=2017|issn=1531-426X|pages=27–52|volume=59|issue=2|doi=10.1111/laps.12017|language=en|first=Jorge I.|last=Domínguez|first2=Ángela Fonseca|last2=Galvis|first3=Chiara|last3=Superti}}</ref>. **ಜನಶಕ್ತಿ ರಾಷ್ಟ್ರೀಯ ಸಭೆಗಾಗಿ ನಡೆಯುವ ಚುನಾವಣೆಯಲ್ಲಿ ಪ್ರತಿ ಸ್ಥಾನಕ್ಕೆ ಒಂದೇ ಅಭ್ಯರ್ಥಿ ಇರ್ತಾರೆ, ಆಣ್ ಅಭ್ಯರ್ಥಿಗಳನ್ನು ಕಮ್ಯುನಿಸ್ಟ್ ಪಕ್ಷದಿಂದ ಬಲವಾಗಿ ನಿಯಂತ್ರಿಸಲ್ಪಟ್ಟ ಸಮಿತಿಗಳು ನೇಮಕ ಮಾಡ್ತಾರೆ.<ref>{{Cite journal|title=Legislative Rules in Electoral Authoritarian Regimes: The Case of Hong Kong’s Legislative Council|url=https://www.journals.uchicago.edu/doi/10.1086/703068|journal=The Journal of Politics|date=2019-07|issn=0022-3816|pages=892–905|volume=81|issue=3|doi=10.1086/703068|first=Regina|last=Smyth|first2=William|last2=Bianco|first3=Kwan Nok|last3=Chan}}</ref>. ಬಹುತೇಕ ಶಾಸನ ಕ್ಷೇತ್ರಗಳು ಸಭೆಗೆ ಬಹು ಮಂದಿ ಪ್ರತಿನಿಧಿಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡ್ತಾರೆ. ಮತದಾರರು ತಮ್ಮ ಮತಪತ್ರದಲ್ಲಿ ವ್ಯಕ್ತಿಪರವಾಗಿ ಅಭ್ಯರ್ಥಿಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಬಹುದು, ಎಲ್ಲ ಅಭ್ಯರ್ಥಿಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಬಹುದು, ಅಥವಾ ಎಲ್ಲ ಪ್ರಶ್ನೆಗಳನ್ನು ಖಾಲಿ ಬಿಟ್ಟುಬಿಡಬಹುದು. ಅಭ್ಯರ್ಥಿಗಳ ವಿರುದ್ಧ ಮತ ಹಾಕಲು ಆಯ್ಕೆ ಇರುವುದಿಲ್ಲ.<ref>{{Cite journal|title=Authoritarian Regimes and Their Permitted Oppositions: Election Day Outcomes in Cuba|url=https://www.cambridge.org/core/journals/latin-american-politics-and-society/article/abs/authoritarian-regimes-and-their-permitted-oppositions-election-day-outcomes-in-cuba/3F9E5B1A4EB059A316A9AB2BB0628216|journal=Latin American Politics and Society|date=2017-07|issn=1531-426X|pages=27–52|volume=59|issue=2|doi=10.1111/laps.12017|language=en|first=Jorge I.|last=Domínguez|first2=Ángela Fonseca|last2=Galvis|first3=Chiara|last3=Superti}}</ref>.ಯಾವುದೇ ರಾಜಕೀಯ ಪಕ್ಷಕ್ಕೂ, ಕಮ್ಯುನಿಸ್ಟ್ ಪಕ್ಷವನ್ನು ಸೇರಿ, ದ್ವೀಪದಲ್ಲಿ ಅಭ್ಯರ್ಥಿಗಳನ್ನು ನಾಮನಿರ್ದೇಶನ ಮಾಡಲು ಅಥವಾ ಪ್ರಚಾರ ನಡೆಸಲು ಅನುಮತಿ ಇಲ್ಲ.<ref>{{Cite web|title=Elections: Latin American Studies: Collections: SSHL|url=http://sshl.ucsd.edu/collections/las/cuba/1990.html|website=sshl.ucsd.edu|accessdate=2025-11-02|archiveurl=https://web.archive.org/web/20070301123039/http://sshl.ucsd.edu/collections/las/cuba/1990.html|archivedate=2007-03-01|url-status=live}}</ref>. ಕುಬಾ ಕಮ್ಯುನಿಸ್ಟ್ ಪಕ್ಷವು 1975 ರಿಂದ ಆರು ಪಕ್ಷ ಮಹಾಸಭಾ ಸಭೆಗಳನ್ನು ನಡೆಸಿದೆ. 2011 ರಲ್ಲಿ, ಪಕ್ಷವು 8,00,000 ಸದಸ್ಯರಿದ್ದು ಎಂದು ಪ್ರಕಟಿಸಿತು, ಮತ್ತು ಪ್ರತಿನಿಧಿಗಳು ಸಾಮಾನ್ಯವಾಗಿ ರಾಜ್ಯ ಮಂಡಳಿ ಮತ್ತು ರಾಷ್ಟ್ರೀಯ ಸಭೆಯ ಕನಿಷ್ಠ ಅರ್ಧವನ್ನು ಹೊಂದಿರುತ್ತಾರೆ. ಉಳಿದ ಸ್ಥಾನಗಳನ್ನು ಪರಂಪರೆಯಂತೆ ಪಕ್ಷ ಹೊಂದಿಲ್ಲದ ಅಭ್ಯರ್ಥಿಗಳಿಂದ ಭರ್ತಿಮಾಡಲಾಗುತ್ತದೆ. ಇತರ ರಾಜಕೀಯ ಪಕ್ಷಗಳು ಅಂತಾರಾಷ್ಟ್ರೀಯ ಮಟ್ಟದಲ್ಲಿ ಪ್ರಚಾರ ಮತ್ತು ಹಣಕಾಸು ಸಂಗ್ರಹಿಸುತ್ತವೆ, ಆದರೆ ಕುಬಾದಲ್ಲಿ ಪ್ರತಿಪಕ್ಷ ಗುಂಪುಗಳ ಚಟುವಟಿಕೆ ಅತಿಮಿತವಾಗಿದೆ. According to International IDEA’s Global State of Democracy (GSoD) Indices and Democracy Tracker, Cuba performs in the low range on overall democratic measures, however, with particular strengths in gender equality<ref>{{Cite web|title=Cuba {{!}} The Global State of Democracy|url=https://www.idea.int/democracytracker/country/cuba|website=www.idea.int|accessdate=2025-11-02|language=en}}</ref>. Moreover, Cuba is considered an authoritarian regime according to The Economist's Democracy Index<ref>{{Cite web|title=Democracy Index 2016 - The Economist Intelligence Unit|url=https://www.eiu.com/public/topical_report.aspx?campaignid=DemocracyIndex2016|website=www.eiu.com|accessdate=2025-11-02|archivedate=2019-11-11|archive-url=https://web.archive.org/web/20191111195322/https://www.eiu.com/public/topical_report.aspx?campaignid=DemocracyIndex2016|url-status=dead}}</ref> and Freedom in the World reports<ref>{{Cite web|title=Wayback Machine|url=https://freedomhouse.org/sites/default/files/Country%20Status%20&%20Ratings%20Overview,%201973-2016.pdf|website=freedomhouse.org|accessdate=2025-11-02|archiveurl=https://web.archive.org/web/20171020075644/https://freedomhouse.org/sites/default/files/Country%20Status%20&%20Ratings%20Overview,%201973-2016.pdf|archivedate=2017-10-20}}</ref>. More specifically, Cuba is considered a military dictatorship in the Democracy-Dictatorship Index, and has been described as "a militarized society" with the armed forces having long been the most powerful institution in the country.<ref>{{Cite web|title=Wayback Machine|url=http://ctp.iccas.miami.edu/Research_Studies/BLatell.pdf|website=ctp.iccas.miami.edu|accessdate=2025-11-02|archiveurl=https://web.archive.org/web/20090326012540/http://ctp.iccas.miami.edu/Research_Studies/BLatell.pdf|archivedate=2009-03-26}}</ref> In February 2013, President of the State Council Raúl Castro announced he would resign in 2018, ending his five-year term, and that he hopes to implement permanent term limits for future Cuban presidents, including age limits<ref>{{Cite web|title=Cuba's Raul Castro announces retirement in 5 years|url=https://www.usatoday.com/story/news/world/2013/02/24/cuba-president-castro-parliament/1943365/|website=USA TODAY|accessdate=2025-11-02|language=en-US}}</ref>. Miguel Díaz-Canel was elected president on 18 April 2018 after the resignation of Raúl Castro. On 19 April 2021, Díaz-Canel became First Secretary of the Communist Party. He is the first non-Castro to be in such top position since the Cuban revolution of 1959.<ref>{{Cite news|url=https://www.reuters.com/world/americas/cuban-president-diaz-canel-made-communist-party-leader-ending-castro-era-2021-04-19/|title=Raul Castro retires but Cuban Communist Party emphasizes continuity|work=Reuters|access-date=2025-11-02|language=en-US}}</ref> === Administrative divisions === The country is subdivided into 15 provinces and one special municipality (Isla de la Juventud). These were formerly part of six larger historical provinces: Pinar del Río, Habana, Matanzas, Las Villas, Camagüey and Oriente. The present subdivisions closely resemble those of the Spanish military provinces during the Cuban Wars of Independence, when the most troublesome areas were subdivided. The provinces are divided into municipalities. {| class="wikitable" | # Pinar del Río # Artemisa # Havana # Mayabeque # Matanzas # Cienfuegos # Villa Clara # Sancti Spíritus | # Ciego de Ávila # Camagüey # Las Tunas # Granma # Holguín # Santiago de Cuba # Guantánamo # Isla de la Juventud |} === Foreign relations === Cuba is a founding member of the United Nations, G77, Non-Aligned Movement, Organisation of African, Caribbean and Pacific States, ALBA, and Organization of American States. Cuba has conducted a foreign policy that is uncharacteristic of such a minor, developing country<ref>{{Cite book|title=To make a world safe for revolution: Cuba's foreign policy|url=https://archive.org/details/tomakeworldsafef0000domi|last=Domínguez|first=Jorge I.|date=1989|publisher=Harvard Univ. Pr|others=Harvard University|isbn=978-0-674-89325-2|location=Cambridge, Mass. London}}</ref>. Under Castro, Cuba was heavily involved in wars in Africa, Central America and Asia. Cuba supported Algeria in 1961–1965<ref>{{Cite journal|title=Cuba's First Venture in Africa: Algeria, 1961–1965|url=https://www.cambridge.org/core/product/identifier/S0022216X00012670/type/journal_article|journal=Journal of Latin American Studies|date=1996-02|issn=0022-216X|pages=159–195|volume=28|issue=1|doi=10.1017/S0022216X00012670|language=en|first=Piero|last=Gleijeses}}</ref> and sent tens of thousands of troops to Angola during the Angolan Civil War<ref>{{Cite book|title=The Cambridge history of the Cold War|last=Leffler|first=Melvyn P.|last2=Westad|first2=Odd Arne|date=2010|publisher=Cambridge university press|isbn=978-0-521-83719-4|location=Cambridge}}</ref>. Other countries that featured Cuban involvement include Ethiopia<ref name=":8">{{Cite book|title=Conflicting missions: Havana, Washington, and Africa, 1959-1976|url=https://archive.org/details/conflictingmissi0000pier|last=Gleijeses|first=Piero|date=2002|publisher=University of North Carolina Press|isbn=978-0-8078-2647-8|series=Envisioning Cuba|location=Chapel Hill}}</ref>, Guinea<ref name=":9">{{Cite journal|title=The First Ambassadors: Cuba's Contribution to Guinea-Bissau's War of Independence|url=https://www.cambridge.org/core/product/identifier/S0022216X96004646/type/journal_article|journal=Journal of Latin American Studies|date=1997-02|issn=0022-216X|pages=45–88|volume=29|issue=1|doi=10.1017/S0022216X96004646|language=en|first=Piero|last=Gleijeses}}</ref>, Guinea-Bissau<ref name=":9" />, Mozambique<ref name=":8" />, and Yemen<ref>{{Cite book|title=The Persian Gulf and the Strait of Hormuz|url=https://archive.org/details/persiangulfstrai0000rama|last=Ramazani|first=Rouhollah K.|date=1979|publisher=Sijthoff & Noordhoff [International Publishers]|isbn=978-90-286-0069-0|series=International straits of the world|location=Alphen aan den Rijn}}</ref>. Lesser-known actions include the 1959 missions to the Dominican Republic<ref name="Waterloo">{{Citation|title=Waterloo|url=https://doi.org/10.2307/j.ctt20ks10f.41|publisher=University of Iowa Press|work=The WPA Guide to 1930s Iowa|accessdate=2025-11-02|pages=313–320}}</ref>. The expedition failed, but a prominent monument to its members was erected in their memory in Santo Domingo by the Dominican government, and they feature prominently at the country's Memorial Museum of the Resistance<ref name="Waterloo"/>. In 2008, the European Union (EU) and Cuba agreed to resume full relations and cooperation activities. Cuba is a founding member of the Bolivarian Alliance for the Americas. At the end of 2012, tens of thousands of Cuban medical personnel worked abroad, with as many as 30,000 doctors in Venezuela alone via the two countries' oil-for-doctors program. In 1996, the United States, then under President Bill Clinton, brought in the Cuban Liberty and Democratic Solidarity Act, better known as the Helms–Burton Act. In 2009, United States President Barack Obama stated on 17 April, in Trinidad and Tobago that "the United States seeks a new beginning with Cuba", and reversed the Bush Administration's prohibition on travel and remittances by Cuban-Americans from the United States to Cuba. Five years later, an agreement between the United States and Cuba, popularly called the "Cuban thaw", brokered in part by Canada and Pope Francis, began the process of restoring international relations between the two countries. They agreed to release political prisoners and the United States began the process of creating an embassy in Havana. This was realized on 30 June 2015, when Cuba and the U.S. reached a deal to reopen embassies in their respective capitals on 20 July 2015 and reestablish diplomatic relations. Earlier in the same year, the White House announced that President Obama would remove Cuba from the American government's list of nations that sponsor terrorism, which Cuba reportedly welcomed as "fair". On 17 September 2017, the United States considered closing its Cuban embassy following mysterious medical symptoms experienced by its staff. In the wake of the Russian invasion of Ukraine and the ongoing international isolation of Russia, Cuba emerged as one of the few countries that maintained friendly relations with the Russian Federation. Cuban leader Miguel Diaz-Canel visited Vladimir Putin in Moscow in November 2022, where the two leaders opened a monument of Fidel Castro, as well as speaking out against U.S. sanctions against Russia and Cuba. ==== Embargo by the United States (1960–present) ==== Since 1960, the U.S. embargo on Cuba stands as one of the longest-running trade and economic measures in bilateral relations history, having endured for almost six decades. This action was initiated in response to a wave of nationalizations that impacted American properties valued at over US$1 billion. The then U.S. President, Dwight Eisenhower, instated an embargo that prohibited all exports to Cuba, with the exception of medicines and certain foods. This measure was intensified in 1962 under the administration of John F. Kennedy, extending the restrictions to Cuban imports, based on the Foreign Assistance Act approved by Congress in 1961. During the Missile Crisis in 1962, the United States even imposed a naval blockade on Cuba, but this was lifted following the resolution of the crisis. The embargo, however, remained in place and has been modified on several occasions over the years. The Cuban Democracy Act of 1992 states that sanctions will continue "so long as it continues to refuse to move toward democratization and greater respect for human rights".<sup>[''non-primary source needed'']</sup> American diplomat Lester D. Mallory wrote an internal memo on 6 April 1960, arguing in favor of an embargo: "The only foreseeable means of alienating internal support is through disenchantment and disaffection based on economic dissatisfaction and hardship. [...] to decrease monetary and real wages, to bring about hunger, desperation and overthrow of government." The UN General Assembly has passed a resolution every year since 1992 condemning the embargo and stating that it violates the Charter of the United Nations and international law. Cuba considers the embargo a human rights violation. The impact and effectiveness of the embargo have been subjects of intense debate. While some argue it has been "extraordinarily porous" and isn't the primary cause of Cuba's economic hardships, others see it as a pressure mechanism aimed at driving change in the Cuban government. According to Arturo Lopez Levy, a professor of international relations, it would be more appropriate to refer to the measure as a "blockade" or "siege", as it goes beyond mere trade restrictions. Other critics of the Cuban government argue that the embargo has been used by the government as an excuse to justify its own economic and political shortcomings. On 17 December 2014, United States President Barack Obama announced the re-establishment of diplomatic relations with Cuba, pushing for Congress to put an end to the embargo, as well as the United States-run Guantanamo Bay detention camp. These diplomatic improvements were later reversed by the Trump Administration, which enacted new rules and re-enforced the business and travel restrictions which were loosened by the Obama Administration. These sanctions were inherited and strengthened by the Biden Administration. Despite the embargo, Cuba has maintained trade relations with other countries. According to 2019 data, China stands as Cuba's main trading partner, followed by countries such as Spain, the Netherlands, Germany, and Cyprus. Cuba's main exports include tobacco, sugar, and alcoholic beverages, while it primarily imports chicken meat, wheat, corn, and condensed milk. === Military === Main article: Cuban Revolutionary Armed Forces As of 2018, Cuba spent about US$91.8 million on its armed forces or 2.9% of its GDP. In 1985, Cuba devoted more than 10% of its GDP to military expenditures. During the Cold War, Cuba built up the largest per capita armed forces in Latin America, funded and equipped by Moscow. From 1975 until the late 1980s, Soviet military assistance enabled Cuba to upgrade its military capabilities. After the loss of Soviet subsidies, Cuba scaled down the numbers of military personnel, from 235,000 in 1994 to about 49,000 in 2021. In 2017, Cuba signed the UN treaty on the Prohibition of Nuclear Weapons. Since signing a defense pact with Belarus in January 2024, Cuba has upgraded its S-125 Pechora air defense systems with Belarusian support. Cuba is the 98th most peaceful country in the world, according to the 2024 Global Peace Index. === Law enforcement === Main article: Law enforcement in Cuba See also: Committees for the Defense of the Revolution and Crime in Cuba All law enforcement agencies are maintained under Cuba's Ministry of the Interior, which is supervised by the Revolutionary Armed Forces. In Cuba, citizens can receive police assistance by dialing "106" on their telephones. The police force, which is referred to as "Policía Nacional Revolucionaria" or PNR is then expected to provide help. The Cuban government also has an agency called the Intelligence Directorate that conducts intelligence operations and maintains close ties with the Russian Federal Security Service. The US Justice Department considers Cuba a significant counterintelligence threat. Civilians are also involved in law enforcement, in a limited capacity. The Committees for the Defense of the Revolution are an official neighborhood watch organization, made up of dedicated citizens who monitor their neighborhoods. Membership is not selective, but leading members are approved by the Cuban Communist Party. === Human rights === In 2003, the European Union (EU) accused the Cuban government of "continuing flagrant violation of human rights and fundamental freedoms". As of 2009, it has continued to call regularly for social and economic reform in Cuba, along with the unconditional release of all political prisoners. Cuba was ranked 19th by the number of imprisoned journalists of any nation in 2021 according to various sources, including the Committee to Protect Journalists and Human Rights Watch. Cuba ranks 171st out of 180 on the 2020 World Press Freedom Index. In July 2010, the unofficial Cuban Human Rights Commission said there were 167 political prisoners in Cuba, a fall from 201 at the start of the year. The head of the commission stated that long prison sentences were being replaced by harassment and intimidation. == ಮೂಲ ಪತಾ == ekl95c1hn59a4hlihom80qc2lipx18y ಟೆಂಪ್ಲೇಟ್:Smalldiv 10 22082 366187 2021-02-21T21:16:41Z en>Jonesey95 0 update per talk page discussion 366187 wikitext text/x-wiki <div style="font-size:85%;">{{#if: 1 | <!-- To make it possible to use the template with newlines at the beginning and end, inside lists, or put lists inside --> {{{1}}} }}</div><includeonly>{{SAFESUBST:#if:{{{1|}}}||[[Category:Pages using small with an empty input parameter]]}}</includeonly><noinclude> {{Documentation}}<!--Categories and interwikis go in the /doc sub-page.--> </noinclude> 4q4fm7ovwl4n6dh35o6cqm2k4gxtwy5 366188 282685 2026-08-21T10:14:29Z ChiK 1136 ೧ revisions imported from [[:en:Template:Smalldiv]] 282685 wikitext text/x-wiki <div style="font-size:85%;">{{#if: 1 | <!-- To make it possible to use the template with newlines at the beginning and end, inside lists, or put lists inside --> {{{1}}} }}</div><includeonly>{{SAFESUBST:#if:{{{1|}}}||[[Category:Pages using small with an empty input parameter]]}}</includeonly><noinclude> {{Documentation}}<!--Categories and interwikis go in the /doc sub-page.--> </noinclude> 4q4fm7ovwl4n6dh35o6cqm2k4gxtwy5 ಬಳಕೆದಾರೆ:Hariprasad Shetty10/ಕಲ್ಪುನಕಳ 2 24391 366121 365974 2026-08-21T03:11:48Z Hariprasad Shetty10 6127 366121 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ === 1669ಡ್ ಹೆಟ್‌ಮಾನ್ ಡೆಮಿಯನ್ ಮ್ನೊಹೊಹ್ರಿಶ್ನಿ ಬಾತುರಿನ್‌ನು ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಉಂದು ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಬೆಳೆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ (1687–1708) ಕಾಲೊಡು ಬಾತುರಿನ್ ಮಸ್ತ್ ಅಭಿವೃದ್ಧಿ ಆಂಡ್. ಪಟ್ಟಣದ ಜನಸಂಖ್ಯೆ ಜಾಸ್ತಿ ಆಯಿನೆರ್ ಬೊಕ್ಕ ಇತ್ತೆ ಮಸ್ತ್ ಚರ್ಚುಲು, ಚಾಪೆಲ್‌ಲು ಬೊಕ್ಕ ರಡ್ಡ್ ಮಠೊಲು ಇತ್ತುಂಡು. ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಜನರಲ್ ಮಿಲಿಟರಿ ಚಾನ್ಸೆಲರಿ ಬೊಕ್ಕ ಜನರಲ್ ಮಿಲಿಟರಿ ಕೋರ್ಟ್ ಕೂಡ ಬಾತುರಿನ್‌ಡ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಝೆಪಾ ಬಾತುರಿನ್ ಕೈತಲ್ದ ಹೊಂಚಾರಿವ್ಕಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಬಲಿಷ್ಠ ಅರಮನೆ ನಿರ್ಮಿಸಾಯೆರ್. ಉಂದು ಅರೆನ ಆಡಳಿತೊದ ಪ್ರಮುಖ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಬಾತುರಿನ್ ದುರಂತ === 1708ಡ್ ಮಹಾ ಉತ್ತರ ಯುದ್ಧದ ಸಮಯೊಡು ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ ರಷ್ಯಾದ ಝಾರ್ ಪೀಟರ್ ದಿ ಗ್ರೇಟ್ಗ್ ವಿರೋಧವಾದ್ ಸ್ವೀಡನ್‌ದ ರಾಜೆ ಚಾರ್ಲ್ಸ್ XIIದ ಒಟ್ಟುಗ್ ಸೇರಾಯೆರ್. ಅಯಿನ ಬುಕ್ಕೊ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ಮುತ್ತಿಗೆ ಪಾಡಿಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1708ರ ನವೆಂಬರ್‌ಡ್ ಅಲೆಕ್ಸಾಂಡರ್ ಮೆನ್ಶಿಕೊವ್ ನೇತೃತ್ವದ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೊಂದು ಪಟ್ಟಣೊನು ನಾಶ ಮಲ್ತೆರ್. ಈ ಘಟನೆನು ಉಕ್ರೇನಿಯನ್ ಇತಿಹಾಸೊಡು '''ಬಾತುರಿನ್ ದುರಂತ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಈ ದಾಳಿಡ್ ಪಟ್ಟಣೊದ್ ಮಸ್ತ್ ಜನಕುಲು ಬೊಕ್ಕ ರಕ್ಷಣಾ ಪಡೆಲು ತೀರುದು ಪೋಯೆರ್. ಬಾತುರಿನ್ ಸಂಪೂರ್ಣವಾದ್ ನಾಶ ಆಯಿನ ಬುಕ್ಕೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿನ [[ಗ್ಲುಖಿವ್]]ಗು ಬದಲಾಯಿಸಾಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಪುನರ್‌ನಿರ್ಮಾಣ ಬೊಕ್ಕ 18ನೇ ಶತಮಾನೊ === ಬಾತುರಿನ್ 1750ರ ದಶಕೊಡು ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್. 1750ಡ್ದ್ 1764ರ ತನಕ ಹೆಟ್‌ಮಾನ್ ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಬಾತುರಿನ್‌ನು ಕುಡ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ರಝುಮೊವ್ಸ್ಕಿದ್ ಕಾಲೊಡು ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಭವ್ಯ ಅರಮನೆ ನಿರ್ಮಾಣ ಆಂಡ್. ಈ ಅರಮನೆನು ವಾಸ್ತುಶಿಲ್ಪಿ ಆಂಡ್ರೆ ಕ್ವಾಸೊವ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕ ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ಅರೆನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1764ಡ್ ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಸ್ವಾಯತ್ತತೆನು ರದ್ದ್ ಮಲ್ತಿನ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ರಾಜಕೀಯ ಮಹತ್ವೊ ಕಡಿಮೆ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಪರಂಪರೆ == ಬಾತುರಿನ್‌ಡ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಕಾಲೊದ ಮಸ್ತ್ ಐತಿಹಾಸಿಕ ಸ್ಮಾರಕೊಲು ಇನಿ ಒರಿದುಂಡು. ಅವುಲೆಡ್ ಮುಖ್ಯವಾದ್ದ್ '''ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ''', '''ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್''', '''ವಾಸಿಲ್ ಕೊಚುಬೈದ ಇಲ್ಲ್''' ಬೊಕ್ಕ ಮಸ್ತ್ ಚರ್ಚುಲೆನ ಅವಶೇಷೊಲುಲಾ ಉಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ ಬಾತುರಿನ್‌ದ ಪ್ರಮುಖ ವಾಸ್ತುಶಿಲ್ಪದ ಸ್ಮಾರಕೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಇಜಿ 18ನೇ ಶತಮಾನೊದ್ ನಿರ್ಮಾಣ ಆಯಿನೆರ್ ಬೊಕ್ಕ ನಂತರೊಡು ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://en.wikipedia.org/wiki/Baturyn |access-date=20 August 2026 }}</ref> == ಹೆಟ್‌ಮಾನ್ ರಾಜಧಾನಿ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶ == ಬಾತುರಿನ್‌ಡ್ '''ಹೆಟ್‌ಮಾನ್ ರಾಜಧಾನಿ''' ಪಂಡಿನ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶ ಉಂಡು. ಉಂದು ಬಾತುರಿನ್‌ದ ಕೋಸಾಕ್ ಕಾಲೊದ ಐತಿಹಾಸಿಕ ಸ್ಮಾರಕೊಲೆನ್ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನ ಮುಖ್ಯ ಕೇಂದ್ರೊ ಆದುಂಡು. ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶೊಡು ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್, ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ, ವಾಸಿಲ್ ಕೊಚುಬೈದ ಇಲ್ಲ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು ಸೇರುದು ಉಂಡು. == ಜನಸಂಖ್ಯೆ == 2022ರ ಅಂದಾಜುದ ಪ್ರಕಾರ ಬಾತುರಿನ್‌ದ ಜನಸಂಖ್ಯೆ '''2,406''' ಇತ್ತ್ಂಡ್.<ref>{{cite web |title=Baturyn |url=https://en.wikipedia.org/wiki/Baturyn |access-date=20 August 2026 }}</ref> == ಸಂಬಂಧಿತ ವಿಷಯೊಲು == * ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ * ಇವಾನ್ ಮಝೆಪಾ * ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ * ಬಾತುರಿನ್ ದುರಂತ * [[ಗ್ಲುಖಿವ್]] * ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm Internet Encyclopedia of Ukraine — Baturyn] * [https://www.history.org.ua/?termin=Baturyn_smt Encyclopedia of the History of Ukraine — Baturyn] ainv1cjshd13dfixw4wl6qve050haf6 366130 366121 2026-08-21T03:14:40Z Hariprasad Shetty10 6127 ಪುಟದಲ್ಲಿರುವ ಎಲ್ಲಾ ಮಾಹಿತಿಯನ್ನೂ ತಗೆಯುತ್ತಿರುವೆ 366130 wikitext text/x-wiki phoiac9h4m842xq45sp7s6u21eteeq1 366143 366130 2026-08-21T06:07:19Z Hariprasad Shetty10 6127 366143 wikitext text/x-wiki {{coord|50|50|12|N|32|39|20|E|display=title}} {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ | architecture_style = ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ | construction_start_date = 1770ರ ದಶಕ | embedded = {{Infobox historic site | embed = yes | designation1 = UKRAINE NATIONAL | designation1_offname = {{lang|uk|Палацово-парковий комплекс “Качанівка”}} | designation1_type = ಇತಿಹಾಸ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪ | designation1_number = 250045-Н }} }} '''ಕಚಾನಿವ್ಕಾ ಅರಮನೆ''' ({{lang-uk|Качанівський палац}}) ಉಕ್ರೇನ್‌ದ [[ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್]]ಡು ಇಪ್ಪುನ ಒಂಜಿ ಪುರಾತನ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯ. ಇಂಚಿನ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು ಸೇರ್ದ್ '''ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ'''ದ ಒಂಜಿ ಮುಖ್ಯ ಭಾಗ ಆಂಡ್. ಉಂದು ಸ್ಮೋಶ್ (Smosh) ನದಿತ್ತ ದಂಡೆಡ್ ಇಪ್ಪುಂಡು. ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ನಿರ್ಮಾಣೊ 18ನೇ ಶತಮಾನೊದ ಕೊನೆಡ್ ಸುರು ಆಂಡ್. ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಇಂಚಿನ ಎಸ್ಟೇಟ್‌ದ ಅಭಿವೃದ್ಧಿಗ್ ಚಾಲನೆ ಕೊರಿಯೆರ್. ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ದ ಯೋಜನೆದ ಆಧಾರೊಡು ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಅರಮನೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಪಿರವುಟು ಪೊಚೇಕಾ, ಟಾರ್ನೋವ್ಸ್ಕಿ ಬೊಕ್ಕ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊಲೆನ ಕಾಲೊಡು ಅರಮನೆದ ರೂಪೊ ಬೊಕ್ಕ ಸಮುಚ್ಚಯೊ ಇನ್ನಷ್ಟು ಅಭಿವೃದ್ಧಿ ಆಯಿನಿ. == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಇತಿಹಾಸ === ಕಚಾನಿವ್ಕಾ ಪ್ರದೇಶೊದ ಇತಿಹಾಸೊ 18ನೇ ಶತಮಾನೊದ ಮಧ್ಯಭಾಗೊಗು ಪಿರವಾಯಿನಿ. ಈ ಪ್ರದೇಶೊ ಫ್ಯೋದೋರ್ ಕಾಚೆನಿವ್ಸ್ಕಿ ಪುದರ್‍ದ್ ಕಚಾನಿವ್ಕಾ ಪುದರ್ ಬತ್ತಿನ ಸಾಧ್ಯತೆ ಉಂಡು. 1770ರ ದಶಕೊಡು ಈ ಎಸ್ಟೇಟ್ ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಕೈತ ಬತ್ತ್ಂಡ್. ಅವೆರೆ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್‌ದ ನಿರ್ಮಾಣೊ ಸುರು ಆಯಿನಿ. ಮೂಲ ಅರಮನೆನ್ ವಾಸ್ತುಶಿಲ್ಪಿ ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ದ ಯೋಜನೆದ ಮೇರೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣ ಕಾರ್ಯೊಡು ಉಕ್ರೇನ್‌ದ ವಾಸ್ತುಶಿಲ್ಪಿ ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಪ್ರಮುಖ ಪಾತ್ರ ವಹಿಸಾಯೆರ್. ಆರಂಭೊಡು ಅರಮನೆಡ್ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್. === ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊ === 1808ಡ್ ಕಚಾನಿವ್ಕಾ ಗ್ರಿಗೋರಿ ಪೊಚೇಕಾ ಬೊಕ್ಕ ಅರೆನ ಕುಟುಂಬೊದ ಕೈತ ಬತ್ತ್ಂಡ್. 1808–1824 ಕಾಲೊಡು ಅರಮನೆನ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಸಮಯೊಡು ಕಟ್ಟಡೊಗು ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪದ ಲಕ್ಷಣೊಲು ಬತ್ತ್ಂಡ್. ಫ್ಲಿಗಲ್, ಗ್ಯಾಲರಿ ಬೊಕ್ಕ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ್ ಸೇರ್ಸಾದ್ ಸಮುಚ್ಚಯೊನು ವಿಸ್ತಾರ ಮಲ್ತೆರ್. ಇಂಚಿನ ಕಾಲೊಡು ಅರಮನೆದ ಮಧ್ಯ ಭಾಗೊಗು ಗುಂಬಜ್ ಬೊಕ್ಕ ಸ್ತಂಭೊಲೆನ ಪೋರ್ಟಿಕೊ ಸೇರ್ಸಾಯಿನಿ. ಎಸ್ಟೇಟ್‌ದ ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊನುಲಾ ಈ ಅವಧಿಡ್ ಸುರು ಮಲ್ತೆರ್. === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕಾಲೊ === 1824ಡ್ ಕಚಾನಿವ್ಕಾ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕೈತ ಬತ್ತ್ಂಡ್. 19ನೇ ಶತಮಾನೊದ ಬಹುಭಾಗೊಡು ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ ಕಚಾನಿವ್ಕಾದ ಮಾಲಕರ್ ಆದು ಇತ್ತೆರ್. ಅವೆರೆ ಕಾಲೊಡು ಕಚಾನಿವ್ಕಾ ಒಂಜಿ ಪ್ರಮುಖ ಸಾಂಸ್ಕೃತಿಕ ಬೊಕ್ಕ ಕಲಾ ಕೇಂದ್ರ ಆತ್ ಅಭಿವೃದ್ಧಿ ಆಯಿನಿ. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ ಕಲಾವಿದರ್, ಬರಹಗಾರರ್, ಸಂಗೀತಗಾರರ್ ಬೊಕ್ಕ ಪಂಡಿತರ್ನ್ ಆಶ್ರಯ ಮಲ್ಪುನ ಮಾಗ್ಗೆಡ್ ಪ್ರಸಿದ್ಧ ಆಯಿನಿ. ಕಚಾನಿವ್ಕಾಗ್ ಬತ್ತಿನ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲೆಡ್ [[ನಿಕೊಲಾಯ್ ಗೊಗೊಲ್]], [[ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ]], [[ಇಲ್ಯಾ ರೆಪಿನ್]], [[ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್]] ಬೊಕ್ಕ [[ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ]] ಮುಂತಾದವುಲು ಉಂಡು. === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಾಂಸ್ಕೃತಿಕ ಕೊಡುಗೆ === ವಾಸಿಲ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕಿರಿಯೆರ್ ಉಕ್ರೇನ್‌ದ ಇತಿಹಾಸ ಬೊಕ್ಕ ಕೋಸಾಕ್ ಪರಂಪರೆದ ಬಗ್ಗೆ ಬೊಕ್ಕ ಆಸಕ್ತಿ ತೋಜಾಯೆರ್. ಅರೆನ್ ಉಕ್ರೇನ್‌ದ ಪುರಾತನ ವಸ್ತು, ಕೋಸಾಕ್ ಆಯುಧೊಲು, ಹೆಟ್ಮನ್‌ಲೆಗ್ ಸಂಬಂಧಪಟ್ಟ ವಸ್ತು ಬೊಕ್ಕ ಕಲಾಕೃತಿಲೆನ ದೊಡ್ಡ ಸಂಗ್ರಹೊ ಇತ್ತ್ಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಸಂಗ್ರಹೊಡು [[ಬೊಹ್ದಾನ್ ಖ್ಮೆಲ್ನಿತ್ಸ್ಕಿ]], [[ಇವಾನ್ ಮಜೇಪಾ]], [[ಪಾವ್ಲೊ ಪೊಲುಬೊಟೊಕ್]] ಬೊಕ್ಕ ಬೇತೆ ಕೋಸಾಕ್ ಹೆಟ್ಮನ್‌ಲೆಗ್ ಸಂಬಂಧಪಟ್ಟ ಆಯುಧೊಲು ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ವಸ್ತುಲು ಇತ್ತ್ಂಡ್. ಪಿರವುಟು ಈ ಸಂಗ್ರಹೊನು ಸಾರ್ವಜನಿಕ ಬಳಕೆಗ್ ಕೊರಿಯೆರ್. == ವಾಸ್ತುಶಿಲ್ಪ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಇಂದಿನ ರೂಪೊ ಒಂಜೇ ಕಾಲೊಡು ನಿರ್ಮಾಣ ಆಯಿನುದಲ್ಲ. ಸುಮಾರು ಒಂಜಿ ಶತಮಾನೊಗು ಮೇಲ್ಪಟ್ಟ ಕಾಲೊಡು ನಡತಿನ ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನರ್‌ನಿರ್ಮಾಣೊಲೆನ ಫಲೊ ಆಂಡ್. 1770ರ ದಶಕೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಮೂಲ ಅರಮನೆಡ್ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್. 1808–1824 ಕಾಲೊಡು ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪದ ಲಕ್ಷಣೊಲೆನ್ ಸೇರ್ಸಾಯಿನಿ. 19ನೇ ಶತಮಾನೊದ ನಂತರದ ಭಾಗೊಡು ಅರಮನೆನ್ ಬೊಕ್ಕೊಂಜಿ ಬಾರಿ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. 1897ಡ್ ಪಾವ್ಲೊ ಹರಿತೋನೆಂಕೊ ಎಸ್ಟೇಟ್‌ದ ಮಾಲಕ ಆತನಂತರ ವಾಸ್ತುಶಿಲ್ಪಿ ಗ್ಯೆರ್ಗಿ ಶೋಲ್ಟ್ಸ್‌ದ ಮಾರ್ಗದರ್ಶನೊಡು ಅರಮನೆಡ್ ಬದಲಾವಣೆ ಮಲ್ತೆರ್. ಕಟ್ಟಡೊಗು ಇಟ್ಟಿಗೆದ ಹೊರಪದರೊ ಕೊರಿಯೆರ್, ಗುಂಬಜ್‌ನು ದಪ್ಪ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಒಳಾಂಗಣೊನು ನವೀಕರಣ ಮಲ್ತೆರ್. ಹೊರಾಂಗಣ ಅಲಂಕಾರೊಡು ನವಶಾಸ್ತ್ರೀಯ ಲಕ್ಷಣೊಲು ಹೆಚ್ಚಾಯಿನಿ. == ಅರಮನೆದ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು == ಅರಮನೆದ ಉತ್ತರ ಬೊಕ್ಕ ದಕ್ಷಿಣ ಭಾಗೊಡು ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಅಥವಾ ಫ್ಲಿಗಲ್ ಉಂಡು. ಈ ಕಟ್ಟಡೊಲು ಅರಮನೆದ ಮುಖ್ಯ ಭಾಗೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತಿನವುಲು. ಪಿರವುಟು ಇಂಚಿನ ಕಟ್ಟಡೊಲೆಗ್ ಇನ್ನಷ್ಟು ಅಂತಸ್ತು ಬೊಕ್ಕ ಗ್ಯಾಲರಿ ಸೇರ್ಸಾಯಿನಿ. ಈ ಕಟ್ಟಡೊಲೆನ ವಿನ್ಯಾಸೊ ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊನು ಸುತ್ತು ಒಂಜಿ ದೊಡ್ಡ ಮುಕ್ತ ಆವರಣೊ (cour d'honneur) ನಿರ್ಮಾಣ ಮಲ್ಪುನ ರೀತಿಡ್ ಇತ್ತ್ಂಡ್. == ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್ == ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊದ ಪ್ರಮುಖ ಕಟ್ಟಡೊಲೆಡ್ '''ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್''' ಒಂಜಿ. ಇಂಚಿನ ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊ 19ನೇ ಶತಮಾನೊದ ಮೊದಲ ಭಾಗೊಡು ನಡತ್ಂಡ್ ಬೊಕ್ಕ 1828ಡ್ ಪೂರ್ಣ ಆಯಿನಿ. ಚರ್ಚ್ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ವಾಸ್ತುಶಿಲ್ಪೊನು ಪೂರೈಸುನ ಒಂಜಿ ಮುಖ್ಯ ಕಟ್ಟಡ ಆಂಡ್. == ಪಾರ್ಕ್ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಜೊತೊಡು ಇಪ್ಪುನ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಅತಿ ಮುಖ್ಯ ಭಾಗೊಲೆಡ್ ಒಂಜಿ. ಉಂದು 18ನೇ ಶತಮಾನೊದ ಕೊನೆಡ್ದ್ 20ನೇ ಶತಮಾನೊದ ಆರಂಭದ ತನಕ್ ಹಲವು ಹಂತೊಲೆಡ್ ಅಭಿವೃದ್ಧಿ ಆಯಿನಿ. ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ಒಟ್ಟು ವಿಸ್ತೀರ್ಣೊ ಸುಮಾರು '''568.63 ಹೆಕ್ಟೇರ್'''. ಇದ್ರೊಡು ಸುಮಾರು '''125.5 ಹೆಕ್ಟೇರ್''' ಜಲ ಪ್ರದೇಶೊ ಆಂಡ್. ಸ್ಮೋಶ್ ನದಿ, ಜಲಾಶಯೊಲು, ಮರೊಲೆನ ಗುಂಪು, ದಾರಿಲು ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು ಪಾರ್ಕ್‌ದ ಭೂದೃಶ್ಯೊನು ರೂಪಿಸುಂಡು. ಪಾರ್ಕ್‌ಡು ಇಂಗ್ಲಿಷ್ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಪಾರ್ಕ್‌ದ ಪ್ರಭಾವೊನು ಕಂಡುಬರ್ಪುಂಡು. ಪ್ರಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ವಿನ್ಯಾಸೊ ಕಚಾನಿವ್ಕಾಗ್ ವಿಶೇಷ ಸೌಂದರ್ಯ ಕೊರ್ತುಂಡು. == ಬೇತೆ ಕಟ್ಟಡೊಲು == ಅರಮನೆ ಬೊಕ್ಕ ಚರ್ಚ್‌ದ ಹೊರತು ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊಡು ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲುಲಾ ಉಂಡು. ಅವುಲೆಡ್: * ಉತ್ತರ ಬೊಕ್ಕ ದಕ್ಷಿಣ ಸೇವಾ ಕಟ್ಟಡೊಲು * ನೀರಿನ ಗೋಪುರೊ * ಆರೆಂಜರಿ * ಏವಿಯರಿ * ಪಾರ್ಕ್‌ದ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು * ಆಲಂಕಾರಿಕ ಮಂಟಪೊಲು * ಐತಿಹಾಸಿಕ ಉದ್ಯಾನ ರಚನೆಲು ಒಟ್ಟು ಸಮುಚ್ಚಯೊಡು 24 ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೀಯ ವಸ್ತುಲು ಇಪ್ಪುನುದ್ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶದ ಅಧಿಕೃತ ಮಾಹಿತಿಡ್ ಪಂಡಾಯಿನಿ. == ಸಾಹಿತ್ಯ ಬೊಕ್ಕ ಕಲೆ == 19ನೇ ಶತಮಾನೊಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಸಾಂಸ್ಕೃತಿಕ ಕೇಂದ್ರೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಅನೇಕ ಕಲಾವಿದರ್ ಬೊಕ್ಕ ಬರಹಗಾರರ್ ಇಂಚ್ ಬತ್ತೆರ್. ಕಚಾನಿವ್ಕಾಗ್ ಸಂಬಂಧಪಟ್ಟ ಪ್ರಮುಖ ವ್ಯಕ್ತಿಲೆಡ್: * [[ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್]] * [[ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ]] * [[ಇಲ್ಯಾ ರೆಪಿನ್]] * [[ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್]] * [[ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ]] ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ ಕಚಾನಿವ್ಕಾದಲ್ಲಿ ವಾಸ್ತವ್ಯ ಹೂಯೆರ್ ಬೊಕ್ಕ ಅರೆನ ಪ್ರಸಿದ್ಧ ಒಪೆರಾ ''[[ಎ ಲೈಫ್ ಫಾರ್ ದಿ ಜಾರ್]]''‌ದ ಕೆಲಸೊಲೆನ್ ಇಂಚ್ ಮಲ್ತೆರ್. == ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊ == 19ನೇ ಶತಮಾನೊದ ಕೊನೆಡ್ ಕಚಾನಿವ್ಕಾ ಸಕ್ಕರೆ ಉದ್ಯಮಿ ಪಾವ್ಲೊ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊದ ಕೈತ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಆಧುನೀಕರಣೊ ನಡತ್ಂಡ್. ವಿದ್ಯುತ್ ಬೊಕ್ಕ ದೂರವಾಣಿ ಮುಂತಾದ ಆಧುನಿಕ ಸೌಲಭ್ಯೊಲೆನ್ ಎಸ್ಟೇಟ್‌ಗು ಪರಿಚಯ ಮಲ್ತೆರ್. ಆದರೆ ಅರಮನೆದ ಐತಿಹಾಸಿಕ ಶಾಸ್ತ್ರೀಯ ಸ್ವರೂಪೊನು ಸಾಧ್ಯವಾದಷ್ಟು ಕಾಪಾಡುನ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. == ಸೋವಿಯತ್ ಕಾಲೊ == 1918ಡ್ ಸೋವಿಯತ್ ಆಡಳಿತೊ ಬತ್ತಿನ ಪಿರವುಟು ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಕರಣ ಆಯಿನಿ. 1918–1920ರ ರಾಜಕೀಯ ಬದಲಾವಣೆಲೆನ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ಪರಿಸ್ಥಿತಿ ಬದಲಾಗ್ತ್ಂಡ್. ಸೋವಿಯತ್ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಎಸ್ಟೇಟ್‌ದ ಕಟ್ಟಡೊಲೆನ್ ಬೇತೆ ಬೇತೆ ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್. ಅರಮನೆದ ಭಾಗೊನು ದಂಡ ವಸಾಹತು ಬೊಕ್ಕ ಕ್ಷಯರೋಗ ಆಸ್ಪತ್ರೆಗ್ ಬಳಕೆ ಮಲ್ತಿನ ಮಾಹಿತಿ ಉಂಡು. ಆದರೆ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಅನೇಕ ಸಹಾಯಕ ಕಟ್ಟಡೊಲು ಉಳಿದುದರಿಂದ ಕಚಾನಿವ್ಕಾ ಇಂದು ಉಕ್ರೇನ್‌ದ ಅತಿ ಎಡ್ಡೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. == ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ == '''ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ''' 24 ನವೆಂಬರ್ 1981ಡ್ ಸ್ಥಾಪನೆ ಆಯಿನಿ. ಇಂಚಿನ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊನು ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಆಧಾರೊಡು ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. 27 ಫೆಬ್ರವರಿ 2001ಡ್ ಕಚಾನಿವ್ಕಾಗ್ '''ರಾಷ್ಟ್ರೀಯ''' ಸಂರಕ್ಷಿತ ಪ್ರದೇಶದ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್. ಈ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಡು ಅರಮನೆ, ಪಾರ್ಕ್, ಚರ್ಚ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು ಸೇರ್ದ್ ಒಂಜಿ ಸಮಗ್ರ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಭೂದೃಶ್ಯೊನು ರೂಪಿಸುಂಡು. == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಚಾನಿವ್ಕಾ ಕೇವಲ ಒಂಜಿ ಅರಮನೆ ಅಲ್ಲ. ಉಂದು ಉಕ್ರೇನ್‌ದ ಲೆಫ್ಟ್-ಬ್ಯಾಂಕ್ ಪ್ರದೇಶೊದ 18ನೇ–20ನೇ ಶತಮಾನೊದ ಅರಮನೆ, ಪಾರ್ಕ್, ಕಲೆ ಬೊಕ್ಕ ಸಾಹಿತ್ಯ ಪರಂಪರೆದ ಒಂಜಿ ಅಪೂರ್ವ ಉದಾಹರಣೆ ಆಂಡ್. ವಿಶೇಷವಾದ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಕಚಾನಿವ್ಕಾ ಕಲಾವಿದರ್, ಬರಹಗಾರರ್, ಸಂಗೀತಗಾರರ್ ಬೊಕ್ಕ ಸಂಶೋಧಕರ್ ಬತ್ತ್ ಸೇರುನ ಸಾಂಸ್ಕೃತಿಕ ಕೇಂದ್ರ ಆಂಡ್. ಇಂಚಿನ ಮಾಗ್ಗೆಡ್ ಕಚಾನಿವ್ಕಾಗ್ ಉಕ್ರೇನ್‌ದ ಸಾಂಸ್ಕೃತಿಕ ಇತಿಹಾಸೊಡು ವಿಶೇಷ ಸ್ಥಾನ ಬತ್ತ್ಂಡ್. == ಸಂರಕ್ಷಣೆ ಬೊಕ್ಕ ಪ್ರವಾಸೋದ್ಯಮ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಇಂದ್ ಐತಿಹಾಸಿಕ, ವಾಸ್ತುಶಿಲ್ಪೀಯ ಬೊಕ್ಕ ಭೂದೃಶ್ಯ ಪರಂಪರೆದ ಸಂರಕ್ಷಣೊನು ಮಲ್ಪುಂಡು. ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊ ಪ್ರವಾಸಿಗರೆಗ್ ಪ್ರಮುಖ ಆಕರ್ಷಣೆ ಆಂಡ್. ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ವಿಶಾಲತೆ, ಅರಮನೆದ ವಾಸ್ತುಶಿಲ್ಪೊ, ಐತಿಹಾಸಿಕ ಚರ್ಚ್ ಬೊಕ್ಕ 19ನೇ ಶತಮಾನೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ಒಟ್ಟುಗು ಸೇರ್ಸಾದ್ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಪರಂಪರೆ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. == ಉಲ್ಲೇಖೊಲು == {{reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://kachanivka.in.ua/ ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kachanivka National Historical and Cultural Reserve}} {{Historic reserves of Chernihiv Oblast}} {{Seven Wonders of Ukraine}} [[Category:ಉಕ್ರೇನ್‌ದ ಅರಮನೆಲು]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:1770ರ ದಶಕೊದ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಉದ್ಯಾನೊಲು]] myhtcar90ylwqrf0qkf73u7xnag48mp 366144 366143 2026-08-21T07:10:00Z Hariprasad Shetty10 6127 366144 wikitext text/x-wiki {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ | architecture_style = ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಬೊಕ್ಕ ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ | construction_start_date = 1770ರ ದಶಕ | embedded = {{Infobox historic site | embed = yes | designation1 = UKRAINE NATIONAL | designation1_offname = {{lang|uk|Палацово-парковий комплекс “Качанівка”}} | designation1_type = ಇತಿಹಾಸ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪ | designation1_number = 250045-Н }} }} '''ಕಚಾನಿವ್ಕಾ ಅರಮನೆ''' ಉಕ್ರೇನ್‌ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡು ಇಪ್ಪುನ ಒಂಜಿ ಪುರಾತನ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಕಟ್ಟಡ. ಉಂದು ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ಇಪ್ಪುನ ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆದುಂಡು. ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಅರಮನೆ-ಪಾರ್ಕ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ.<ref name="ESU">{{cite web |title=Качанівка Національний історико-культурний заповідник |url=https://esu.com.ua/article-11368 |website=Енциклопедія Сучасної України |publisher=Інститут енциклопедичних досліджень НАН України |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಇತಿಹಾಸ === ಕಚಾನಿವ್ಕಾ ಪುದರ್‌ದ್ 18ನೇ ಶತಮಾನೊದ ದಾಖಲೆಲೆಡ್ ತೋಜುದು ಬರ್ಪುಂಡು. 1740ರ ದಶಕೊಡು ಈ ಪ್ರದೇಶೊ ಕೋಚಾನಿವ್ಸ್ಕಿ ಭೂಮಾಲಕರೆನ ಸ್ವಾಮ್ಯೊಡು ಇತ್ತ್ಂಡ್. 1770ರ ದಶಕೊಡು ಎಸ್ಟೇಟ್ ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾಡ್ ಮಲ್ಲ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ನಿರ್ಮಾಣೊ ಸುರು ಆಂಡ್.<ref name="EIU">{{cite web |last=Кот |first=С. І. |title=Качанівка |url=https://resource.history.org.ua/cgi-bin/eiu/history.exe?C21COM=S&I21DBN=EIU&P21DBN=EIU&S21CNR=20&S21COLORTERMS=0&S21FMT=eiu_all&S21P01=0&S21P02=0&S21P03=TRN%3D&S21REF=10&S21STN=1&S21STR=Kachanivka&Z21ID= |website=Енциклопедія історії України |publisher=Інститут історії України НАН України |year=2007 |access-date=21 August 2026 }}</ref> ಅರಮನೆದ ಯೋಜನೆ ಮಾಸ್ಕೋ ವಾಸ್ತುಶಿಲ್ಪಿ ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ಗ್ ಸಂಬಂಧಪಟ್ಟಿದ್ದು, ನಿರ್ಮಾಣ ಕಾರ್ಯೊನು ಉಕ್ರೇನ್‌ದ ವಾಸ್ತುಶಿಲ್ಪಿ ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಮೇಲ್ವಿಚಾರಣೆ ಮಲ್ತೆರ್. ಅರಮನೆದ ಆರಂಭಿಕ ರೂಪೊಡು ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್.<ref name="EIU"/> 1770ರ ದಶಕೊಡು ರುಮ್ಯಾಂತ್ಸೆವ್ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಪಾರ್ಕ್‌ದ ಮೂಲ ವಿನ್ಯಾಸೊಲಾ ರೂಪುಗೊಂಡುದುಂಡು. ಪಿರವುಡು ಬೇತೆ ಮಾಲಕೆರ್ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ವಿಸ್ತಾರ ಮಲ್ತೆರ್.<ref name="ESU"/> === ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊ === 1808ಡ್ ಕಚಾನಿವ್ಕಾ ಗ್ರಿಗೋರಿ ಪೊಚೇಕಾ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಅದಗ ಅರಮನೆಗ್ ರಡ್ಡನೇ ಅಂತಸ್ತು, ಗ್ಯಾಲರಿಲು ಬೊಕ್ಕ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲೆನ್ ಸೇರ್ಸಾಯೆರ್.<ref name="history100">{{cite web |title=Качанівка |url=https://uahistory.co/book/100-great-wonders-of-ukraine/17.php |website=100 Великих чудес України |access-date=21 August 2026 }}</ref> ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ವಾಸ್ತುಶಿಲ್ಪೊ ಬೊಕ್ಕ ಭೂದೃಶ್ಯೊದ ಅಭಿವೃದ್ಧಿಗ್ ಬೊಕ್ಕೊಂಜಿ ಮುಖ್ಯ ಹಂತ ಬತ್ತ್ಂಡ್. ಅರೆನ ಪಿರವುಡು ಎಸ್ಟೇಟ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್.<ref name="EIU"/> === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ === 1824ಡ್ ಕಚಾನಿವ್ಕಾ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸ್ವಾಮ್ಯೊಗು ಬತ್ತ್ಂಡ್. 19ನೇ ಶತಮಾನೊಡು ಈ ಕುಟುಂಬೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅಭಿವೃದ್ಧಿಗ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರಿಯೆರ್.<ref name="EIU"/> ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಸಾಹಿತ್ಯ, ಕಲೆ, ಸಂಗೀತ ಬೊಕ್ಕ ವಿಜ್ಞಾನೊದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದ್ ಬೆಳದ್ಂಡ್. ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ, ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್, ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ, ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್, ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್, ಇಲ್ಯಾ ರೆಪಿನ್ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು ಕಚಾನಿವ್ಕಾಗ್ ಬತ್ತೆರ್.<ref name="EIU"/> === ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊ === 1897ಡ್ ಕಚಾನಿವ್ಕಾ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಆಧುನೀಕರಣೊ ನಡತ್ಂಡ್. ಈ ಅವಧಿಡ್ ಕಟ್ಟಡೊದ ಹೊರಾಂಗಣ ಬೊಕ್ಕ ಒಳಾಂಗಣೊಡು ಕೆಲವು ಬದಲಾವಣೆಲು ಮಲ್ತೆರ್.<ref name="ESU"/> == ವಾಸ್ತುಶಿಲ್ಪ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಇತ್ತೆದ ರೂಪೊ ಒಂಜೇ ಕಾಲೊಡು ನಿರ್ಮಾಣ ಆತುಜಿ. 18ನೇ ಶತಮಾನೊದ ಅಕೆರಿಡ್ದ್ 20ನೇ ಶತಮಾನೊದ ಆರಂಭದ ತನಕ್ ನಡತಿನ ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನರ್‌ನಿರ್ಮಾಣೊಲೆನ ಆಂಡ್.<ref name="ESU"/>ಕಕಕಕಕಕಕಕಕಕಕಕಕಕಕಕ ಅರಮನೆದ ಮೂಲ ನಿರ್ಮಾಣೊ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಶೈಲಿಡ್ ನಡತ್ಂಡ್. ಪಿರವುಟು ಮಾಲಕರ್ ಅರಮನೆನ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಕಾರಣೊಡು ಕಟ್ಟಡೊಡು ಶಾಸ್ತ್ರೀಯ ಬೊಕ್ಕ ನವಶಾಸ್ತ್ರೀಯ ಲಕ್ಷಣೊಲುಲಾ ಕಂಡುಬರ್ಪುಂಡು.<ref name="100wonders"/> ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊಗು ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಗ್ಯಾಲರಿಲು ಸೇರ್ಸಾಯಿನಿ. ಸಮುಚ್ಚಯೊದ ವಿನ್ಯಾಸೊ ಅರಮನೆ, ಸೇವಾ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಪಾರ್ಕ್‌ನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ರೂಪುಗೊಂಡ್ಂಡ್.<ref name="ESU"/> == ಅರಮನೆದ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು == ಅರಮನೆದ ಮುಖ್ಯ ಕಟ್ಟಡೊದ ಎರಡೂ ಬದಿಡ್ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಅಥವಾ ಫ್ಲಿಗಲ್ ಉಂಡು. ಈ ಕಟ್ಟಡೊಲು ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತಿನವುಲು. ಎಸ್ಟೇಟ್‌ದ ಅಭಿವೃದ್ಧಿ ನಡತಿನ ಕಾಲೊಡು ಸೇವಾ ಕಟ್ಟಡೊಲು, ಗೃಹೊಲು ಬೊಕ್ಕ ಬೇತೆ ಉಪಯೋಗೊಲೆಗ್ ಬೇಕಾಯಿನ ರಚನೆಲು ಸೇರ್ಸಾಯಿನಿ. ಇವುಲು ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಐತಿಹಾಸಿಕ ಸ್ವರೂಪೊನು ಪೂರೈಸುಂಡು.<ref name="ESU"/> == ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಡು ಒಂಜಿ ಐತಿಹಾಸಿಕ ಚರ್ಚ್‌ಲಾ ಉಂಡು. 19ನೇ ಶತಮಾನೊದ ಆರಂಭೊಡು ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಉಂದು ಸಮುಚ್ಚಯೊದ ವಾಸ್ತುಶಿಲ್ಪೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆಂಡ್.<ref name="ESU"/> ಚರ್ಚ್ ಬೊಕ್ಕ ಅರಮನೆದ ಸಂಬಂಧೊ ಕಚಾನಿವ್ಕಾ ಎಸ್ಟೇಟ್‌ದ ಧಾರ್ಮಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಜೀವನೊನು ತೋಜಾಯೆರುಂಡು. == ಪಾರ್ಕ್ == ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್ ಅರಮನೆದಷ್ಟೇ ಪ್ರಮುಖ ಪರಂಪರೆ ಆಂಡ್. ಪಾರ್ಕ್‌ದ ನಿರ್ಮಾಣೊ 18ನೇ ಶತಮಾನೊದ ಕೊನೆಡ್ ಸುರು ಆಯಿನಿ ಬೊಕ್ಕ ಪಿರವುಟು ಹಲವಾರು ತಲೆಮಾರುಲೆನ ಮಾಲಕರ್ ಅದನ್ನು ಅಭಿವೃದ್ಧಿ ಮಲ್ತೆರ್.<ref name="OfficialReserve">{{cite web |title=Про організатора — Національний історико-культурний заповідник «Качанівка» |url=https://kachanivka.event.net.ua/site/about |website=Національний історико-культурний заповідник «Качанівка» |access-date=21 August 2026 }}</ref> ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ವಿಸ್ತೀರ್ಣೊ ಸುಮಾರು '''568.63 ಹೆಕ್ಟೇರ್'''. ಇಂಚ್ ಸುಮಾರು '''125.5 ಹೆಕ್ಟೇರ್''' ಜಲ ಪ್ರದೇಶೊ ಉಂಡು. ಪಾರ್ಕ್ ಸ್ಮೋಶ್ ನದಿತ್ತ ದಂಡೆಡ್ ವಿಸ್ತಾರ ಆಯಿನಿ.<ref name="ParkArea">{{cite web |title=Качанівка |url=https://secinfchounb.blogspot.com/2012/03/2011-30.html |website=Чернігівська обласна універсальна наукова бібліотека |access-date=21 August 2026 }}</ref> ಪಾರ್ಕ್‌ಡು ದೊಡ್ಡ ಮರೊಲೆನ ಗುಂಪು, ಜಲಾಶಯೊಲು, ದಾರಿಲು, ಭೂದೃಶ್ಯ ಪ್ರದೇಶೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು ಉಂಡು. ಪ್ರಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ವಿನ್ಯಾಸೊ ಕಚಾನಿವ್ಕಾಗ್ ವಿಶೇಷ ಮಹತ್ವ ಕೊರ್ತುಂಡು.<ref name="OfficialReserve"/> == ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು == ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಚರ್ಚ್‌ದ ಹೊರತು ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲುಲಾ ಉಂಡು. ಅವುಲೆಡ್: * ಸೇವಾ ಕಟ್ಟಡೊಲು * ಫ್ಲಿಗಲ್ * ಆರೆಂಜರಿ * ಏವಿಯರಿ * ನೀರಿನ ಗೋಪುರ * ಉದ್ಯಾನೊದ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು * ಮಂಟಪೊಲು * ಪಾರ್ಕ್‌ದ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಕಚಾನಿವ್ಕಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಸಮುಚ್ಚಯೊಡು ಹಲವಾರು ಕಟ್ಟಡೊಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು.<ref name="OfficialReserve"/> == ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಂಗ್ರಹೊ == ವಾಸಿಲ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕಿರಿಯೆರ್ ಉಕ್ರೇನ್‌ದ ಇತಿಹಾಸ ಬೊಕ್ಕ ಸಂಸ್ಕೃತಿದ ಬಗ್ಗೆ ವಿಶೇಷ ಆಸಕ್ತಿ ತೋಜಾಯೆರ್. ಅರೆನ್ ಕೋಸಾಕ್ ಇತಿಹಾಸೊಗು ಸಂಬಂಧಪಟ್ಟ ಆಯುಧೊಲು, ದಾಖಲೆಲು, ಕಲಾಕೃತಿಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ವಸ್ತುಲೆನ ದೊಡ್ಡ ಸಂಗ್ರಹೊನು ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="EIU"/> ಈ ಸಂಗ್ರಹೊ ಉಕ್ರೇನ್‌ದ ಹೆಟ್ಮನ್ ಬೊಕ್ಕ ಕೋಸಾಕ್ ಪರಂಪರೆದ ಅಧ್ಯಯನೊಗು ಮಹತ್ವದ ಕೊಡುಗೆ ಕೊರ್ತುಂಡು. ಪಿರವುಟು ಸಂಗ್ರಹೊದ ಹಲವು ಭಾಗೊಲೆನ್ ಸಾರ್ವಜನಿಕ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲೆಗ್ ವರ್ಗಾವಣೆ ಮಲ್ತೆರ್. == ಸಾಹಿತ್ಯ ಬೊಕ್ಕ ಕಲೆ == 19ನೇ ಶತಮಾನೊಡು ಕಚಾನಿವ್ಕಾ ಒಂಜಿ ಪ್ರಮುಖ ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾತ್ಮಕ ಕೇಂದ್ರ ಆಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಅನೇಕ ಪ್ರಸಿದ್ಧ ಬರಹಗಾರರ್, ಕಲಾವಿದರ್ ಬೊಕ್ಕ ಸಂಗೀತಗಾರರ್ ಇಂಚ್ ಬತ್ತೆರ್.<ref name="EIU"/> ಕಚಾನಿವ್ಕಾಗ್ ಭೇಟಿ ಕೊರಿನ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲೆಡ್: * [[ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ]] * [[ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್]] * [[ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ]] * [[ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್]] * [[ವ್ಲಾಡಿಮಿರ್ ಮಕೋವ್ಸ್ಕಿ]] * [[ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್]] * [[ಇಲ್ಯಾ ರೆಪಿನ್]] * [[ವಿಕ್ಟರ್ ಶ್ಟೆನ್‌ಬರ್ಗ್]] * [[ಮೈಖೈಲೊ ಮಕ್ಸಿಮೊವಿಚ್]] * [[ದಿಮಿತ್ರೊ ಯವೊರ್ನಿತ್ಸ್ಕಿ]] <ref name="EIU"/> ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ ಕಚಾನಿವ್ಕಾದಲ್ಲಿ ವಾಸ್ತವ್ಯ ಹೂಡಿ ಸಂಗೀತ ಕೃತಿಗಳ ಮೇಲೆ ಕೆಲಸ ಮಲ್ತೆರ್. ಅರೆನ ಕಚಾನಿವ್ಕಾ ವಾಸ್ತವ್ಯೊ ಉಕ್ರೇನ್‌ದ ಸಂಗೀತ ಇತಿಹಾಸೊಡು ಗಮನಾರ್ಹ ಘಟನೆ ಆಂಡ್.<ref name="EIU"/> == 20ನೇ ಶತಮಾನ == 1918ಡ್ ಸೋವಿಯತ್ ಆಡಳಿತೊ ಬತ್ತಿನ ಪಿರವುಟು ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಕರಣ ಆಯಿನಿ. 1918–1920ರ ರಾಜಕೀಯ ಬದಲಾವಣೆಲೆನ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ಆಡಳಿತೊ ಹಲವು ಬಾರಿ ಬದಲಾಗ್ಂಡ್.<ref name="ESU"/> 1941ರ ಸೆಪ್ಟೆಂಬರ್‌ಡ್ದ್ 1943ರ ಸೆಪ್ಟೆಂಬರ್ ತನಕ್ ಕಚಾನಿವ್ಕಾ ಜರ್ಮನ್ ಆಕ್ರಮಣೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್.<ref name="ESU"/> ಸೋವಿಯತ್ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಎಸ್ಟೇಟ್‌ದ ಕಟ್ಟಡೊಲೆನ್ ಬೇತೆ ಬೇತೆ ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್. ಈ ಬಳಕೆಲೆನ ಕಾರಣೊಡು ಸಮುಚ್ಚಯೊದ ಕೆಲವು ಭಾಗೊಲೆಗ್ ಹಾನಿ ಆಯಿನಿ.<ref name="OfficialReserve"/> == ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ == 24 ನವೆಂಬರ್ 1981ಡ್ ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಆಧಾರೊಡು '''ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ''' ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್. ಈ ನಿರ್ಧಾರೊದ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ್ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನುದ್ ಆಂಡ್.<ref name="OfficialReserve"/> 1981ಡ್ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಸ್ಥಾಪನೆ ಆಯಿನ ಪಿರವುಟು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಆತ್ ಗುರುತಾಯಿನಿ.<ref name="ESU"/> == ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ == 27 ಫೆಬ್ರವರಿ 2001ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷ ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ಅವರು ಹೊರಡಾಯಿನ №118/2001 ಆದೇಶೊದ ಮೇರೆಡ್ ಕಚಾನಿವ್ಕಾ ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಗು '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ'''ದ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="President">{{cite web |title=Про надання статусу національного Державному історико-культурному заповіднику «Качанівка» |url=https://www.president.gov.ua/documents/1182001-543 |website=Президент України |date=27 February 2001 |access-date=21 August 2026 }}</ref> ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಅಮೂಲ್ಯ ವಸ್ತುಲೆನ್ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಜೊತೊಗು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಮಹತ್ವೊನು ಉಲ್ಲೇಖ ಮಲ್ತ್ಂಡ್.<ref name="President"/> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಎಡದಂಡೆ ಪ್ರದೇಶೊಡು ಉಳಿದಿರುವ ಪ್ರಮುಖ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಅರಮನೆ ವಾಸ್ತುಶಿಲ್ಪೊ, ಉದ್ಯಾನಕಲೆ, ಸಾಹಿತ್ಯ, ಸಂಗೀತ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸಾಂಸ್ಕೃತಿಕ ಇತಿಹಾಸೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ತಾಣ ಆಂಡ್.<ref name="ESU"/> Encyclopedia of Modern Ukraine ಪ್ರಕಾರ ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಎಸ್ಟೇಟ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಎಡದಂಡೆ ಉಕ್ರೇನ್‌ಡು ತನ್ನ ಐತಿಹಾಸಿಕ ಸಮಗ್ರತೊನು ಉಳಿಸಿರುನ ಅಪರೂಪದ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಆಂಡ್.<ref name="ESU"/> 19ನೇ ಶತಮಾನೊಡು ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಬುದ್ಧಿಜೀವಿ, ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾವಿದರೆನ ಭೇಟಿದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆಂಡ್.<ref name="EIU"/> == ಪ್ರವಾಸೋದ್ಯಮ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಇಂದು ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪ್ರವಾಸೋದ್ಯಮೊದ ಪ್ರಮುಖ ತಾಣ ಆಂಡ್. ಅರಮನೆ, ವಿಶಾಲ ಪಾರ್ಕ್, ಐತಿಹಾಸಿಕ ಚರ್ಚ್, ಪಾರ್ಕ್‌ದ ರಚನೆಲು ಬೊಕ್ಕ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ಪ್ರವಾಸಿಗರೆಗ್ ಪ್ರಮುಖ ಆಕರ್ಷಣೆಲು ಆಂಡ್.<ref name="OfficialReserve"/> ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಕಾರಣೊಡು ಕಚಾನಿವ್ಕಾ ಕೇವಲ ಒಂಜಿ ಕಟ್ಟಡ ಅಲ್ಲದೆ ಒಂಜಿ ಸಮಗ್ರ ಸಾಂಸ್ಕೃತಿಕ ಭೂದೃಶ್ಯ ತಾಣ ಆಂಡ್. == ಸಂರಕ್ಷಣೆ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಮುಖ್ಯ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್, ಚರ್ಚ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಬಳಕೆ ಆಂಡ್.<ref name="President"/> 2001ರ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡುಲಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಪುನರುಜ್ಜೀವನೊಗು ಹೆಚ್ಚುವರಿ ಕ್ರಮೊಲೆನ್ ಮಲ್ಪುನ ಅಗತ್ಯೊನು ಸೂಚಿಸಾಯಿನಿ.<ref name="President"/> == ಉಲ್ಲೇಖೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://kachanivka.event.net.ua/site/about ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * [https://kachanivka.in.ua/ ಕಚಾನಿವ್ಕಾ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kachanivka National Historical and Cultural Reserve}} [[Category:ಉಕ್ರೇನ್‌ದ ಅರಮನೆಲು]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:1770ರ ದಶಕೊದ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಉದ್ಯಾನೊಲು]] ev8e0jkl9i5v4kyrrq9mebwq2f80ak3 366146 366144 2026-08-21T07:46:33Z Hariprasad Shetty10 6127 /* ವಾಸ್ತುಶಿಲ್ಪ */ 366146 wikitext text/x-wiki {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ | architecture_style = ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಬೊಕ್ಕ ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ | construction_start_date = 1770ರ ದಶಕ | embedded = {{Infobox historic site | embed = yes | designation1 = UKRAINE NATIONAL | designation1_offname = {{lang|uk|Палацово-парковий комплекс “Качанівка”}} | designation1_type = ಇತಿಹಾಸ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪ | designation1_number = 250045-Н }} }} '''ಕಚಾನಿವ್ಕಾ ಅರಮನೆ''' ಉಕ್ರೇನ್‌ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡು ಇಪ್ಪುನ ಒಂಜಿ ಪುರಾತನ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಕಟ್ಟಡ. ಉಂದು ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ಇಪ್ಪುನ ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆದುಂಡು. ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಅರಮನೆ-ಪಾರ್ಕ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ.<ref name="ESU">{{cite web |title=Качанівка Національний історико-культурний заповідник |url=https://esu.com.ua/article-11368 |website=Енциклопедія Сучасної України |publisher=Інститут енциклопедичних досліджень НАН України |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಇತಿಹಾಸ === ಕಚಾನಿವ್ಕಾ ಪುದರ್‌ದ್ 18ನೇ ಶತಮಾನೊದ ದಾಖಲೆಲೆಡ್ ತೋಜುದು ಬರ್ಪುಂಡು. 1740ರ ದಶಕೊಡು ಈ ಪ್ರದೇಶೊ ಕೋಚಾನಿವ್ಸ್ಕಿ ಭೂಮಾಲಕರೆನ ಸ್ವಾಮ್ಯೊಡು ಇತ್ತ್ಂಡ್. 1770ರ ದಶಕೊಡು ಎಸ್ಟೇಟ್ ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾಡ್ ಮಲ್ಲ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ನಿರ್ಮಾಣೊ ಸುರು ಆಂಡ್.<ref name="EIU">{{cite web |last=Кот |first=С. І. |title=Качанівка |url=https://resource.history.org.ua/cgi-bin/eiu/history.exe?C21COM=S&I21DBN=EIU&P21DBN=EIU&S21CNR=20&S21COLORTERMS=0&S21FMT=eiu_all&S21P01=0&S21P02=0&S21P03=TRN%3D&S21REF=10&S21STN=1&S21STR=Kachanivka&Z21ID= |website=Енциклопедія історії України |publisher=Інститут історії України НАН України |year=2007 |access-date=21 August 2026 }}</ref> ಅರಮನೆದ ಯೋಜನೆ ಮಾಸ್ಕೋ ವಾಸ್ತುಶಿಲ್ಪಿ ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ಗ್ ಸಂಬಂಧಪಟ್ಟಿದ್ದು, ನಿರ್ಮಾಣ ಕಾರ್ಯೊನು ಉಕ್ರೇನ್‌ದ ವಾಸ್ತುಶಿಲ್ಪಿ ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಮೇಲ್ವಿಚಾರಣೆ ಮಲ್ತೆರ್. ಅರಮನೆದ ಆರಂಭಿಕ ರೂಪೊಡು ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್.<ref name="EIU"/> 1770ರ ದಶಕೊಡು ರುಮ್ಯಾಂತ್ಸೆವ್ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಪಾರ್ಕ್‌ದ ಮೂಲ ವಿನ್ಯಾಸೊಲಾ ರೂಪುಗೊಂಡುದುಂಡು. ಪಿರವುಡು ಬೇತೆ ಮಾಲಕೆರ್ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ವಿಸ್ತಾರ ಮಲ್ತೆರ್.<ref name="ESU"/> === ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊ === 1808ಡ್ ಕಚಾನಿವ್ಕಾ ಗ್ರಿಗೋರಿ ಪೊಚೇಕಾ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಅದಗ ಅರಮನೆಗ್ ರಡ್ಡನೇ ಅಂತಸ್ತು, ಗ್ಯಾಲರಿಲು ಬೊಕ್ಕ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲೆನ್ ಸೇರ್ಸಾಯೆರ್.<ref name="history100">{{cite web |title=Качанівка |url=https://uahistory.co/book/100-great-wonders-of-ukraine/17.php |website=100 Великих чудес України |access-date=21 August 2026 }}</ref> ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ವಾಸ್ತುಶಿಲ್ಪೊ ಬೊಕ್ಕ ಭೂದೃಶ್ಯೊದ ಅಭಿವೃದ್ಧಿಗ್ ಬೊಕ್ಕೊಂಜಿ ಮುಖ್ಯ ಹಂತ ಬತ್ತ್ಂಡ್. ಅರೆನ ಪಿರವುಡು ಎಸ್ಟೇಟ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್.<ref name="EIU"/> === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ === 1824ಡ್ ಕಚಾನಿವ್ಕಾ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸ್ವಾಮ್ಯೊಗು ಬತ್ತ್ಂಡ್. 19ನೇ ಶತಮಾನೊಡು ಈ ಕುಟುಂಬೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅಭಿವೃದ್ಧಿಗ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರಿಯೆರ್.<ref name="EIU"/> ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಸಾಹಿತ್ಯ, ಕಲೆ, ಸಂಗೀತ ಬೊಕ್ಕ ವಿಜ್ಞಾನೊದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದ್ ಬೆಳದ್ಂಡ್. ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ, ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್, ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ, ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್, ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್, ಇಲ್ಯಾ ರೆಪಿನ್ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು ಕಚಾನಿವ್ಕಾಗ್ ಬತ್ತೆರ್.<ref name="EIU"/> === ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊ === 1897ಡ್ ಕಚಾನಿವ್ಕಾ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಆಧುನೀಕರಣೊ ನಡತ್ಂಡ್. ಈ ಅವಧಿಡ್ ಕಟ್ಟಡೊದ ಹೊರಾಂಗಣ ಬೊಕ್ಕ ಒಳಾಂಗಣೊಡು ಕೆಲವು ಬದಲಾವಣೆಲು ಮಲ್ತೆರ್.<ref name="ESU"/> == ವಾಸ್ತುಶಿಲ್ಪ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಇತ್ತೆದ ರೂಪೊ ಒಂಜೇ ಕಾಲೊಡು ನಿರ್ಮಾಣ ಆತುಜಿ. 18ನೇ ಶತಮಾನೊದ ಅಕೆರಿಡ್ದ್ 20ನೇ ಶತಮಾನೊದ ಆರಂಭದ ತನಕ್ ನಡತಿನ ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನರ್‌ನಿರ್ಮಾಣೊಲೆನ ಆಂಡ್.<ref name="ESU"/> ಅರಮನೆದ ಮೂಲ ನಿರ್ಮಾಣೊ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಶೈಲಿಡ್ ನಡತ್ಂಡ್. ಪಿರವುಟು ಮಾಲಕರ್ ಅರಮನೆನ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಕಾರಣೊಡು ಕಟ್ಟಡೊಡು ಶಾಸ್ತ್ರೀಯ ಬೊಕ್ಕ ನವಶಾಸ್ತ್ರೀಯ ಲಕ್ಷಣೊಲುಲಾ ಕಂಡುಬರ್ಪುಂಡು.<ref name="100wonders"/> ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊಗು ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಗ್ಯಾಲರಿಲು ಸೇರ್ಸಾಯಿನಿ. ಸಮುಚ್ಚಯೊದ ವಿನ್ಯಾಸೊ ಅರಮನೆ, ಸೇವಾ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಪಾರ್ಕ್‌ನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ರೂಪುಗೊಂಡ್ಂಡ್.<ref name="ESU"/> == ಅರಮನೆದ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು == ಅರಮನೆದ ಮುಖ್ಯ ಕಟ್ಟಡೊದ ಎರಡೂ ಬದಿಡ್ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಅಥವಾ ಫ್ಲಿಗಲ್ ಉಂಡು. ಈ ಕಟ್ಟಡೊಲು ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತಿನವುಲು. ಎಸ್ಟೇಟ್‌ದ ಅಭಿವೃದ್ಧಿ ನಡತಿನ ಕಾಲೊಡು ಸೇವಾ ಕಟ್ಟಡೊಲು, ಗೃಹೊಲು ಬೊಕ್ಕ ಬೇತೆ ಉಪಯೋಗೊಲೆಗ್ ಬೇಕಾಯಿನ ರಚನೆಲು ಸೇರ್ಸಾಯಿನಿ. ಇವುಲು ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಐತಿಹಾಸಿಕ ಸ್ವರೂಪೊನು ಪೂರೈಸುಂಡು.<ref name="ESU"/> == ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಡು ಒಂಜಿ ಐತಿಹಾಸಿಕ ಚರ್ಚ್‌ಲಾ ಉಂಡು. 19ನೇ ಶತಮಾನೊದ ಆರಂಭೊಡು ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಉಂದು ಸಮುಚ್ಚಯೊದ ವಾಸ್ತುಶಿಲ್ಪೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆಂಡ್.<ref name="ESU"/> ಚರ್ಚ್ ಬೊಕ್ಕ ಅರಮನೆದ ಸಂಬಂಧೊ ಕಚಾನಿವ್ಕಾ ಎಸ್ಟೇಟ್‌ದ ಧಾರ್ಮಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಜೀವನೊನು ತೋಜಾಯೆರುಂಡು. == ಪಾರ್ಕ್ == ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್ ಅರಮನೆದಷ್ಟೇ ಪ್ರಮುಖ ಪರಂಪರೆ ಆಂಡ್. ಪಾರ್ಕ್‌ದ ನಿರ್ಮಾಣೊ 18ನೇ ಶತಮಾನೊದ ಕೊನೆಡ್ ಸುರು ಆಯಿನಿ ಬೊಕ್ಕ ಪಿರವುಟು ಹಲವಾರು ತಲೆಮಾರುಲೆನ ಮಾಲಕರ್ ಅದನ್ನು ಅಭಿವೃದ್ಧಿ ಮಲ್ತೆರ್.<ref name="OfficialReserve">{{cite web |title=Про організатора — Національний історико-культурний заповідник «Качанівка» |url=https://kachanivka.event.net.ua/site/about |website=Національний історико-культурний заповідник «Качанівка» |access-date=21 August 2026 }}</ref> ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ವಿಸ್ತೀರ್ಣೊ ಸುಮಾರು '''568.63 ಹೆಕ್ಟೇರ್'''. ಇಂಚ್ ಸುಮಾರು '''125.5 ಹೆಕ್ಟೇರ್''' ಜಲ ಪ್ರದೇಶೊ ಉಂಡು. ಪಾರ್ಕ್ ಸ್ಮೋಶ್ ನದಿತ್ತ ದಂಡೆಡ್ ವಿಸ್ತಾರ ಆಯಿನಿ.<ref name="ParkArea">{{cite web |title=Качанівка |url=https://secinfchounb.blogspot.com/2012/03/2011-30.html |website=Чернігівська обласна універсальна наукова бібліотека |access-date=21 August 2026 }}</ref> ಪಾರ್ಕ್‌ಡು ದೊಡ್ಡ ಮರೊಲೆನ ಗುಂಪು, ಜಲಾಶಯೊಲು, ದಾರಿಲು, ಭೂದೃಶ್ಯ ಪ್ರದೇಶೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು ಉಂಡು. ಪ್ರಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ವಿನ್ಯಾಸೊ ಕಚಾನಿವ್ಕಾಗ್ ವಿಶೇಷ ಮಹತ್ವ ಕೊರ್ತುಂಡು.<ref name="OfficialReserve"/> == ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು == ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಚರ್ಚ್‌ದ ಹೊರತು ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲುಲಾ ಉಂಡು. ಅವುಲೆಡ್: * ಸೇವಾ ಕಟ್ಟಡೊಲು * ಫ್ಲಿಗಲ್ * ಆರೆಂಜರಿ * ಏವಿಯರಿ * ನೀರಿನ ಗೋಪುರ * ಉದ್ಯಾನೊದ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು * ಮಂಟಪೊಲು * ಪಾರ್ಕ್‌ದ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಕಚಾನಿವ್ಕಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಸಮುಚ್ಚಯೊಡು ಹಲವಾರು ಕಟ್ಟಡೊಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು.<ref name="OfficialReserve"/> == ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಂಗ್ರಹೊ == ವಾಸಿಲ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕಿರಿಯೆರ್ ಉಕ್ರೇನ್‌ದ ಇತಿಹಾಸ ಬೊಕ್ಕ ಸಂಸ್ಕೃತಿದ ಬಗ್ಗೆ ವಿಶೇಷ ಆಸಕ್ತಿ ತೋಜಾಯೆರ್. ಅರೆನ್ ಕೋಸಾಕ್ ಇತಿಹಾಸೊಗು ಸಂಬಂಧಪಟ್ಟ ಆಯುಧೊಲು, ದಾಖಲೆಲು, ಕಲಾಕೃತಿಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ವಸ್ತುಲೆನ ದೊಡ್ಡ ಸಂಗ್ರಹೊನು ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="EIU"/> ಈ ಸಂಗ್ರಹೊ ಉಕ್ರೇನ್‌ದ ಹೆಟ್ಮನ್ ಬೊಕ್ಕ ಕೋಸಾಕ್ ಪರಂಪರೆದ ಅಧ್ಯಯನೊಗು ಮಹತ್ವದ ಕೊಡುಗೆ ಕೊರ್ತುಂಡು. ಪಿರವುಟು ಸಂಗ್ರಹೊದ ಹಲವು ಭಾಗೊಲೆನ್ ಸಾರ್ವಜನಿಕ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲೆಗ್ ವರ್ಗಾವಣೆ ಮಲ್ತೆರ್. == ಸಾಹಿತ್ಯ ಬೊಕ್ಕ ಕಲೆ == 19ನೇ ಶತಮಾನೊಡು ಕಚಾನಿವ್ಕಾ ಒಂಜಿ ಪ್ರಮುಖ ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾತ್ಮಕ ಕೇಂದ್ರ ಆಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಅನೇಕ ಪ್ರಸಿದ್ಧ ಬರಹಗಾರರ್, ಕಲಾವಿದರ್ ಬೊಕ್ಕ ಸಂಗೀತಗಾರರ್ ಇಂಚ್ ಬತ್ತೆರ್.<ref name="EIU"/> ಕಚಾನಿವ್ಕಾಗ್ ಭೇಟಿ ಕೊರಿನ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲೆಡ್: * [[ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ]] * [[ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್]] * [[ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ]] * [[ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್]] * [[ವ್ಲಾಡಿಮಿರ್ ಮಕೋವ್ಸ್ಕಿ]] * [[ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್]] * [[ಇಲ್ಯಾ ರೆಪಿನ್]] * [[ವಿಕ್ಟರ್ ಶ್ಟೆನ್‌ಬರ್ಗ್]] * [[ಮೈಖೈಲೊ ಮಕ್ಸಿಮೊವಿಚ್]] * [[ದಿಮಿತ್ರೊ ಯವೊರ್ನಿತ್ಸ್ಕಿ]] <ref name="EIU"/> ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ ಕಚಾನಿವ್ಕಾದಲ್ಲಿ ವಾಸ್ತವ್ಯ ಹೂಡಿ ಸಂಗೀತ ಕೃತಿಗಳ ಮೇಲೆ ಕೆಲಸ ಮಲ್ತೆರ್. ಅರೆನ ಕಚಾನಿವ್ಕಾ ವಾಸ್ತವ್ಯೊ ಉಕ್ರೇನ್‌ದ ಸಂಗೀತ ಇತಿಹಾಸೊಡು ಗಮನಾರ್ಹ ಘಟನೆ ಆಂಡ್.<ref name="EIU"/> == 20ನೇ ಶತಮಾನ == 1918ಡ್ ಸೋವಿಯತ್ ಆಡಳಿತೊ ಬತ್ತಿನ ಪಿರವುಟು ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಕರಣ ಆಯಿನಿ. 1918–1920ರ ರಾಜಕೀಯ ಬದಲಾವಣೆಲೆನ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ಆಡಳಿತೊ ಹಲವು ಬಾರಿ ಬದಲಾಗ್ಂಡ್.<ref name="ESU"/> 1941ರ ಸೆಪ್ಟೆಂಬರ್‌ಡ್ದ್ 1943ರ ಸೆಪ್ಟೆಂಬರ್ ತನಕ್ ಕಚಾನಿವ್ಕಾ ಜರ್ಮನ್ ಆಕ್ರಮಣೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್.<ref name="ESU"/> ಸೋವಿಯತ್ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಎಸ್ಟೇಟ್‌ದ ಕಟ್ಟಡೊಲೆನ್ ಬೇತೆ ಬೇತೆ ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್. ಈ ಬಳಕೆಲೆನ ಕಾರಣೊಡು ಸಮುಚ್ಚಯೊದ ಕೆಲವು ಭಾಗೊಲೆಗ್ ಹಾನಿ ಆಯಿನಿ.<ref name="OfficialReserve"/> == ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ == 24 ನವೆಂಬರ್ 1981ಡ್ ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಆಧಾರೊಡು '''ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ''' ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್. ಈ ನಿರ್ಧಾರೊದ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ್ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನುದ್ ಆಂಡ್.<ref name="OfficialReserve"/> 1981ಡ್ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಸ್ಥಾಪನೆ ಆಯಿನ ಪಿರವುಟು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಆತ್ ಗುರುತಾಯಿನಿ.<ref name="ESU"/> == ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ == 27 ಫೆಬ್ರವರಿ 2001ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷ ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ಅವರು ಹೊರಡಾಯಿನ №118/2001 ಆದೇಶೊದ ಮೇರೆಡ್ ಕಚಾನಿವ್ಕಾ ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಗು '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ'''ದ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="President">{{cite web |title=Про надання статусу національного Державному історико-культурному заповіднику «Качанівка» |url=https://www.president.gov.ua/documents/1182001-543 |website=Президент України |date=27 February 2001 |access-date=21 August 2026 }}</ref> ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಅಮೂಲ್ಯ ವಸ್ತುಲೆನ್ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಜೊತೊಗು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಮಹತ್ವೊನು ಉಲ್ಲೇಖ ಮಲ್ತ್ಂಡ್.<ref name="President"/> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಎಡದಂಡೆ ಪ್ರದೇಶೊಡು ಉಳಿದಿರುವ ಪ್ರಮುಖ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಅರಮನೆ ವಾಸ್ತುಶಿಲ್ಪೊ, ಉದ್ಯಾನಕಲೆ, ಸಾಹಿತ್ಯ, ಸಂಗೀತ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸಾಂಸ್ಕೃತಿಕ ಇತಿಹಾಸೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ತಾಣ ಆಂಡ್.<ref name="ESU"/> Encyclopedia of Modern Ukraine ಪ್ರಕಾರ ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಎಸ್ಟೇಟ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಎಡದಂಡೆ ಉಕ್ರೇನ್‌ಡು ತನ್ನ ಐತಿಹಾಸಿಕ ಸಮಗ್ರತೊನು ಉಳಿಸಿರುನ ಅಪರೂಪದ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಆಂಡ್.<ref name="ESU"/> 19ನೇ ಶತಮಾನೊಡು ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಬುದ್ಧಿಜೀವಿ, ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾವಿದರೆನ ಭೇಟಿದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆಂಡ್.<ref name="EIU"/> == ಪ್ರವಾಸೋದ್ಯಮ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಇಂದು ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪ್ರವಾಸೋದ್ಯಮೊದ ಪ್ರಮುಖ ತಾಣ ಆಂಡ್. ಅರಮನೆ, ವಿಶಾಲ ಪಾರ್ಕ್, ಐತಿಹಾಸಿಕ ಚರ್ಚ್, ಪಾರ್ಕ್‌ದ ರಚನೆಲು ಬೊಕ್ಕ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ಪ್ರವಾಸಿಗರೆಗ್ ಪ್ರಮುಖ ಆಕರ್ಷಣೆಲು ಆಂಡ್.<ref name="OfficialReserve"/> ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಕಾರಣೊಡು ಕಚಾನಿವ್ಕಾ ಕೇವಲ ಒಂಜಿ ಕಟ್ಟಡ ಅಲ್ಲದೆ ಒಂಜಿ ಸಮಗ್ರ ಸಾಂಸ್ಕೃತಿಕ ಭೂದೃಶ್ಯ ತಾಣ ಆಂಡ್. == ಸಂರಕ್ಷಣೆ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಮುಖ್ಯ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್, ಚರ್ಚ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಬಳಕೆ ಆಂಡ್.<ref name="President"/> 2001ರ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡುಲಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಪುನರುಜ್ಜೀವನೊಗು ಹೆಚ್ಚುವರಿ ಕ್ರಮೊಲೆನ್ ಮಲ್ಪುನ ಅಗತ್ಯೊನು ಸೂಚಿಸಾಯಿನಿ.<ref name="President"/> == ಉಲ್ಲೇಖೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://kachanivka.event.net.ua/site/about ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * [https://kachanivka.in.ua/ ಕಚಾನಿವ್ಕಾ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kachanivka National Historical and Cultural Reserve}} [[Category:ಉಕ್ರೇನ್‌ದ ಅರಮನೆಲು]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:1770ರ ದಶಕೊದ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಉದ್ಯಾನೊಲು]] 8w5gzilz5vr8jlnnzdldybv3879ljdo 366151 366146 2026-08-21T08:20:55Z Hariprasad Shetty10 6127 366151 wikitext text/x-wiki {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ | architecture_style = ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಬೊಕ್ಕ ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ | construction_start_date = 1770ರ ದಶಕ | embedded = {{Infobox historic site | embed = yes | designation1 = UKRAINE NATIONAL | designation1_offname = {{lang|uk|Палацово-парковий комплекс “Качанівка”}} | designation1_type = ಇತಿಹಾಸ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪ | designation1_number = 250045-Н }} }} '''ಕಚಾನಿವ್ಕಾ ಅರಮನೆ''' ಉಕ್ರೇನ್‌ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡು ಇಪ್ಪುನ ಒಂಜಿ ಪುರಾತನ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಕಟ್ಟಡ. ಉಂದು ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ಇಪ್ಪುನ ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆದುಂಡು. ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಅರಮನೆ-ಪಾರ್ಕ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ.<ref name="ESU">{{cite web |title=Качанівка Національний історико-культурний заповідник |url=https://esu.com.ua/article-11368 |website=Енциклопедія Сучасної України |publisher=Інститут енциклопедичних досліджень НАН України |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಇತಿಹಾಸ === ಕಚಾನಿವ್ಕಾ ಪುದರ್‌ದ್ 18ನೇ ಶತಮಾನೊದ ದಾಖಲೆಲೆಡ್ ತೋಜುದು ಬರ್ಪುಂಡು. 1740ರ ದಶಕೊಡು ಈ ಪ್ರದೇಶೊ ಕೋಚಾನಿವ್ಸ್ಕಿ ಭೂಮಾಲಕರೆನ ಸ್ವಾಮ್ಯೊಡು ಇತ್ತ್ಂಡ್. 1770ರ ದಶಕೊಡು ಎಸ್ಟೇಟ್ ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾಡ್ ಮಲ್ಲ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ನಿರ್ಮಾಣೊ ಸುರು ಆಂಡ್.<ref name="EIU">{{cite web |last=Кот |first=С. І. |title=Качанівка |url=https://resource.history.org.ua/cgi-bin/eiu/history.exe?C21COM=S&I21DBN=EIU&P21DBN=EIU&S21CNR=20&S21COLORTERMS=0&S21FMT=eiu_all&S21P01=0&S21P02=0&S21P03=TRN%3D&S21REF=10&S21STN=1&S21STR=Kachanivka&Z21ID= |website=Енциклопедія історії України |publisher=Інститут історії України НАН України |year=2007 |access-date=21 August 2026 }}</ref> ಅರಮನೆದ ಯೋಜನೆ ಮಾಸ್ಕೋ ವಾಸ್ತುಶಿಲ್ಪಿ ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ಗ್ ಸಂಬಂಧಪಟ್ಟಿದ್ದು, ನಿರ್ಮಾಣ ಕಾರ್ಯೊನು ಉಕ್ರೇನ್‌ದ ವಾಸ್ತುಶಿಲ್ಪಿ ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಮೇಲ್ವಿಚಾರಣೆ ಮಲ್ತೆರ್. ಅರಮನೆದ ಆರಂಭಿಕ ರೂಪೊಡು ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್.<ref name="EIU"/> 1770ರ ದಶಕೊಡು ರುಮ್ಯಾಂತ್ಸೆವ್ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಪಾರ್ಕ್‌ದ ಮೂಲ ವಿನ್ಯಾಸೊಲಾ ರೂಪುಗೊಂಡುದುಂಡು. ಪಿರವುಡು ಬೇತೆ ಮಾಲಕೆರ್ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ವಿಸ್ತಾರ ಮಲ್ತೆರ್.<ref name="ESU"/> === ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊ === 1808ಡ್ ಕಚಾನಿವ್ಕಾ ಗ್ರಿಗೋರಿ ಪೊಚೇಕಾ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಅದಗ ಅರಮನೆಗ್ ರಡ್ಡನೇ ಅಂತಸ್ತು, ಗ್ಯಾಲರಿಲು ಬೊಕ್ಕ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲೆನ್ ಸೇರ್ಸಾಯೆರ್.<ref name="history100">{{cite web |title=Качанівка |url=https://uahistory.co/book/100-great-wonders-of-ukraine/17.php |website=100 Великих чудес України |access-date=21 August 2026 }}</ref> ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ವಾಸ್ತುಶಿಲ್ಪೊ ಬೊಕ್ಕ ಭೂದೃಶ್ಯೊದ ಅಭಿವೃದ್ಧಿಗ್ ಬೊಕ್ಕೊಂಜಿ ಮುಖ್ಯ ಹಂತ ಬತ್ತ್ಂಡ್. ಅರೆನ ಪಿರವುಡು ಎಸ್ಟೇಟ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್.<ref name="EIU"/> === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ === 1824ಡ್ ಕಚಾನಿವ್ಕಾ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸ್ವಾಮ್ಯೊಗು ಬತ್ತ್ಂಡ್. 19ನೇ ಶತಮಾನೊಡು ಈ ಕುಟುಂಬೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅಭಿವೃದ್ಧಿಗ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರಿಯೆರ್.<ref name="EIU"/> ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಸಾಹಿತ್ಯ, ಕಲೆ, ಸಂಗೀತ ಬೊಕ್ಕ ವಿಜ್ಞಾನೊದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದ್ ಬೆಳದ್ಂಡ್. ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ, ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್, ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ, ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್, ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್, ಇಲ್ಯಾ ರೆಪಿನ್ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು ಕಚಾನಿವ್ಕಾಗ್ ಬತ್ತೆರ್.<ref name="EIU"/> === ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊ === 1897ಡ್ ಕಚಾನಿವ್ಕಾ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಆಧುನೀಕರಣೊ ನಡತ್ಂಡ್. ಈ ಅವಧಿಡ್ ಕಟ್ಟಡೊದ ಹೊರಾಂಗಣ ಬೊಕ್ಕ ಒಳಾಂಗಣೊಡು ಕೆಲವು ಬದಲಾವಣೆಲು ಮಲ್ತೆರ್.<ref name="ESU"/> == ವಾಸ್ತುಶಿಲ್ಪ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಇತ್ತೆದ ರೂಪೊ ಒಂಜೇ ಕಾಲೊಡು ನಿರ್ಮಾಣ ಆತುಜಿ. 18ನೇ ಶತಮಾನೊದ ಅಕೆರಿಡ್ದ್ 20ನೇ ಶತಮಾನೊದ ಸುರುತ ತನಕ ನಡತಿನ ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್.<ref name="ESU"/> ಅರಮನೆದ ಮೂಲ ನಿರ್ಮಾಣೊ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಶೈಲಿಡ್ ನಡತ್ಂಡ್. ಪಿರವುಡು ಮಾಲಕೆರ್ ಅರಮನೆನ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಕಾರಣೊಡು ಕಟ್ಟಡೊಡು ಶಾಸ್ತ್ರೀಯ ಬೊಕ್ಕ ನವಶಾಸ್ತ್ರೀಯ ಲಕ್ಷಣೊಲುಲಾ ತೋಜಿದ್ ಬರ್ಪುಂಡು.<ref name="100wonders"/> ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊಗು ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಗ್ಯಾಲರಿಲು ಸೇರ್ಸಾಯಿನಿ. ಸಮುಚ್ಚಯೊದ ವಿನ್ಯಾಸೊ ಅರಮನೆ, ಸೇವಾ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಪಾರ್ಕ್‌ನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ಮಾರ್ಪಾಡು ಆತುಂಡು.<ref name="ESU"/> == ಅರಮನೆದ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು == ಅರಮನೆದ ಮುಖ್ಯ ಕಟ್ಟಡೊದ ರಡ್ಡ್ ಬದಿಡ್ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಅತ್ತುಂಡ ಫ್ಲಿಗಲ್ ಉಂಡು. ಈ ಕಟ್ಟಡೊಲು ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ದೆರ್. ಎಸ್ಟೇಟ್‌ದ ಅಭಿವೃದ್ಧಿ ನಡತಿನ ಕಾಲೊಡು ಸೇವಾ ಕಟ್ಟಡೊಲು, ಗೃಹೊಲು ಬೊಕ್ಕ ಬೇತೆ ಉಪಯೋಗೊಲೆಗ್ ಬೇಕಾಯಿನ ರಚನೆಲು ಸೇರ್ಸಾಯಿನಿ. ಇವುಲು ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಐತಿಹಾಸಿಕ ಸ್ವರೂಪೊನು ಪೂರೈಸುಂಡು.<ref name="ESU"/> == ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಡು ಒಂಜಿ ಐತಿಹಾಸಿಕ ಚರ್ಚ್‌ಲಾ ಉಂಡು. 19ನೇ ಶತಮಾನೊದ ಆರಂಭೊಡು ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಉಂದು ಸಮುಚ್ಚಯೊದ ವಾಸ್ತುಶಿಲ್ಪೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆಂಡ್.<ref name="ESU"/> ಚರ್ಚ್ ಬೊಕ್ಕ ಅರಮನೆದ ಸಂಬಂಧೊ ಕಚಾನಿವ್ಕಾ ಎಸ್ಟೇಟ್‌ದ ಧಾರ್ಮಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಜೀವನೊನು ತೋಜಾಯೆರುಂಡು. == ಪಾರ್ಕ್ == ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್ ಅರಮನೆದತೇ ಪ್ರಮುಖ ಪರಂಪರೆ ಆದುಂಡು. ಪಾರ್ಕ್‌ದ ನಿರ್ಮಾಣೊ 18ನೇ ಶತಮಾನೊದ ಅಕೇರಿಡ್ ಸುರು ಆಯಿನಿ ಬೊಕ್ಕ ಪಿರಾವುಡು ಅವೆತೊ ತಲೆಮಾರುದ ಮಾಲಕೆರ್ ಅವೆನ್ ಅಭಿವೃದ್ಧಿ ಮಲ್ತೆರ್.<ref name="OfficialReserve">{{cite web |title=Про організатора — Національний історико-культурний заповідник «Качанівка» |url=https://kachanivka.event.net.ua/site/about |website=Національний історико-культурний заповідник «Качанівка» |access-date=21 August 2026 }}</ref> ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ವಿಸ್ತೀರ್ಣೊ ಸುಮಾರು '''568.63 ಹೆಕ್ಟೇರ್'''. ಇಂಚ ಸುಮಾರು '''125.5 ಹೆಕ್ಟೇರ್''' ನೀರ್ ದ ಪ್ರದೇಶೊ ಉಂಡು. ಪಾರ್ಕ್ ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ವಿಸ್ತಾರ ಆದುಂಡು.<ref name="ParkArea">{{cite web |title=Качанівка |url=https://secinfchounb.blogspot.com/2012/03/2011-30.html |website=Чернігівська обласна універсальна наукова бібліотека |access-date=21 August 2026 }}</ref> ಪಾರ್ಕ್‌ಡು ಮಲ್ಲ ಮರೊಲೆನ ಗುಂಪು, ಜಲಾಶಯೊಲು, ತಾದಿಲು, ಭೂದೃಶ್ಯ ಪ್ರದೇಶೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು ಉಂಡು. ಪ್ರಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ವಿನ್ಯಾಸೊ ಕಚಾನಿವ್ಕಾಗ್ ವಿಶೇಷ ಮಹತ್ವ ಕೊರ್ತುಂಡು.<ref name="OfficialReserve"/> == ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು == ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಚರ್ಚ್‌ದ ಹೊರತು ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲುಲಾ ಉಂಡು. ಅವುಲೆಡ್: * ಸೇವಾ ಕಟ್ಟಡೊಲು * ಫ್ಲಿಗಲ್ * ಆರೆಂಜರಿ * ಏವಿಯರಿ * ನೀರಿನ ಗೋಪುರ * ಉದ್ಯಾನೊದ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು * ಮಂಟಪೊಲು * ಪಾರ್ಕ್‌ದ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಕಚಾನಿವ್ಕಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಸಮುಚ್ಚಯೊಡು ಮಸ್ತ್ ಕಟ್ಟಡೊಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು.<ref name="OfficialReserve"/> == ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಂಗ್ರಹೊ == ವಾಸಿಲ್ ಟಾರ್ನೋವ್ಸ್ಕಿ (ಕಿರಿಯ) ಉಕ್ರೇನ್‌ದ ಇತಿಹಾಸ ಬೊಕ್ಕ ಸಂಸ್ಕೃತಿದ ಬಗ್ಗೆ ವಿಶೇಷ ಆಸಕ್ತಿ ತೋಜಾದೆರ್. ಆರ್ ಕೋಸಾಕ್ ಇತಿಹಾಸೊಗು ಸಂಬಂಧಪಡುದಿನ ಆಯುಧೊಲು, ದಾಖಲೆಲು, ಕಲಾಕೃತಿಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ವಸ್ತುಲೆನ ಮಲ್ಲ ಸಂಗ್ರಹೊನು ನಿರ್ಮಾಣ ಮಲ್ದೆರ್.<ref name="EIU"/> ಈ ಸಂಗ್ರಹೊ ಉಕ್ರೇನ್‌ದ ಹೆಟ್ಮನ್ ಬೊಕ್ಕ ಕೋಸಾಕ್ ಪರಂಪರೆದ ಅಧ್ಯಯನೊಗು ಮಹತ್ವದ ಕೊಡುಗೆ ಕೊರ್ತುಂಡು. ಪಿರವುದ ಸಂಗ್ರಹೊದ ಕೆಲವು ಭಾಗೊಲೆನ್ ಸಾರ್ವಜನಿಕ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲೆಗ್ ವರ್ಗಾವಣೆ ಮಲ್ತುದೆರ್. == ಸಾಹಿತ್ಯ ಬೊಕ್ಕ ಕಲೆ == 19ನೇ ಶತಮಾನೊಡು ಕಚಾನಿವ್ಕಾ ಒಂಜಿ ಪ್ರಮುಖ ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾತ್ಮಕ ಕೇಂದ್ರ ಆಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಅವೆತೋ ಪ್ರಸಿದ್ಧ ಬರಹಗಾರೆರ್, ಕಲಾವಿದೆರೆರ್ ಬೊಕ್ಕ ಸಂಗೀತಗಾರೆರೆಗ್ ಆಶ್ರಯ ಕೊರ್ತೆರ್.<ref name="EIU"/> ಕಚಾನಿವ್ಕಾಗ್ ಭೇಟಿ ಕೊರಿನ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು: * ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ * ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್ * ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ * ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್ * ವ್ಲಾಡಿಮಿರ್ ಮಕೋವ್ಸ್ಕಿ * ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್ * ಇಲ್ಯಾ ರೆಪಿನ್ * ವಿಕ್ಟರ್ ಶ್ಟೆನ್‌ಬರ್ಗ್ * ಮೈಖೈಲೊ ಮಕ್ಸಿಮೊವಿಚ್ * ದಿಮಿತ್ರೊ ಯವೊರ್ನಿತ್ಸ್ಕಿ <ref name="EIU"/> ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ ಕಚಾನಿವ್ಕಾದಲ್ಲಿ ವಾಸ್ತವ್ಯ ಹೂಡುದು ಸಂಗೀತ ಕೃತಿಕುಲೆನ ಮಿತ್ತ್ ಕೆಲಸ ಮಲ್ತೆರ್. ಅರೆನ ಕಚಾನಿವ್ಕಾ ವಾಸ್ತವ್ಯೊ ಉಕ್ರೇನ್‌ದ ಸಂಗೀತ ಇತಿಹಾಸೊಡು ಗಮನಾರ್ಹ ಘಟನೆ ಆದುಂಡು.<ref name="EIU"/> == 20ನೇ ಶತಮಾನ == 1918ಡ್ ಸೋವಿಯತ್ ಆಡಳಿತೊ ಬತ್ತಿನ ಪಿರಾವುಡು ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಕರಣ ಆಯಿನಿ. 1918–1920ರ ರಾಜಕೀಯ ಬದಲಾವಣೆಲೆನ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ಆಡಳಿತೊ ಹಲವು ಬಾರಿ ಬದಲಾತುಂಡು.<ref name="ESU"/> 1941ರ ಸೆಪ್ಟೆಂಬರ್‌ಡ್ದ್ 1943ರ ಸೆಪ್ಟೆಂಬರ್ ತನಕ್ ಕಚಾನಿವ್ಕಾ ಜರ್ಮನ್ ಆಕ್ರಮಣೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್.<ref name="ESU"/> ಸೋವಿಯತ್ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಎಸ್ಟೇಟ್‌ದ ಕಟ್ಟಡೊಲೆನ್ ಬೇತೆ ಬೇತೆ ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್. ಈ ಬಳಕೆಲೆನ ಕಾರಣೊಡು ಸಮುಚ್ಚಯೊದ ಕೆಲವು ಭಾಗೊಲೆಗ್ ಹಾನಿ ಲಾ ಆತುಂಡು.<ref name="OfficialReserve"/> == ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ == 24 ನವೆಂಬರ್ 1981ಡ್ ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಆಧಾರೊಡು '''ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ''' ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್. ಈ ನಿರ್ಧಾರೊದ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನವು.<ref name="OfficialReserve"/> 1981ಡ್ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಸ್ಥಾಪನೆ ಆಯಿನ ಪಿರವುಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಆದ್ ಗುರುತುಗು ಬೈದುಂಡು.<ref name="ESU"/> == ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ == 27 ಫೆಬ್ರವರಿ 2001ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷ ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ಆರ್ ಪಿದಾಡುಪಾಯಿನ №118/2001 ಆದೇಶೊದ ಮೇರೆಡ್ ಕಚಾನಿವ್ಕಾ ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಗು '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ'''ದ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="President">{{cite web |title=Про надання статусу національного Державному історико-культурному заповіднику «Качанівка» |url=https://www.president.gov.ua/documents/1182001-543 |website=Президент України |date=27 February 2001 |access-date=21 August 2026 }}</ref> ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಅಮೂಲ್ಯ ವಸ್ತುಲೆನ್ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಜೊತೊಗು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಮಹತ್ವೊನು ಉಲ್ಲೇಕ ಮಲ್ತುಂಡು.<ref name="President"/> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಎಡದಂಡೆ ಪ್ರದೇಶೊಡು ಉಳಿದಿರುವ ಪ್ರಮುಖ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಅರಮನೆ ವಾಸ್ತುಶಿಲ್ಪೊ, ಉದ್ಯಾನಕಲೆ, ಸಾಹಿತ್ಯ, ಸಂಗೀತ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸಾಂಸ್ಕೃತಿಕ ಇತಿಹಾಸೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ತಾಣ ಆಂಡ್.<ref name="ESU"/> ಆಧುನಿಕ ಉಕ್ರೇನ್ ದ ವಿಶ್ವಕೋಶದ ಪ್ರಕಾರ ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಎಸ್ಟೇಟ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. ಉಕ್ರೇನ್‌ಡು ಅರೆನ ಐತಿಹಾಸಿಕ ಸಮಗ್ರತೊನು ಒರಿಪಾದಿನ ಅಪರೂಪದ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಆದುಂಡು.<ref name="ESU"/> 19ನೇ ಶತಮಾನೊಡು ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಬುದ್ಧಿಜೀವಿ, ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾವಿದರೆನ ಭೇಟಿದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದುಂಡು.<ref name="EIU"/> == ಪ್ರವಾಸೋದ್ಯಮ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಇತ್ತೆ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪ್ರವಾಸೋದ್ಯಮೊದ ಪ್ರಮುಖ ತಾಣ ಆದುಂಡು. ಅರಮನೆ, ವಿಶಾಲ ಪಾರ್ಕ್, ಐತಿಹಾಸಿಕ ಚರ್ಚ್, ಪಾರ್ಕ್‌ದ ರಚನೆಲು ಬೊಕ್ಕ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ಪ್ರವಾಸಿಗರೆಗ್ ಪ್ರಮುಖ ಆಕರ್ಷಣೆಲು ಆದುಂಡು.<ref name="OfficialReserve"/> ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಕಾರಣೊಡು ಕಚಾನಿವ್ಕಾ ಕೇವಲ ಒಂಜಿ ಕಟ್ಟಡ ಅತ್ತಂದೆ ಒಂಜಿ ಸಮಗ್ರ ಸಾಂಸ್ಕೃತಿಕ ಭೂದೃಶ್ಯ ತಾಣ ಆದುಂಡು. == ಸಂರಕ್ಷಣೆ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಮುಖ್ಯ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್, ಚರ್ಚ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಬಳಕೆ ಆದುಂಡು.<ref name="President"/> 2001ರ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡುಲಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಪುನರುಜ್ಜೀವನೊಗು ಹೆಚ್ಚುವರಿ ಕ್ರಮೊಲೆನ್ ಮಲ್ಪುನ ಅಗತ್ಯೊನು ಸೂಚಿಸಾದೆರ್.<ref name="President"/> == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://kachanivka.event.net.ua/site/about ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * [https://kachanivka.in.ua/ ಕಚಾನಿವ್ಕಾ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kachanivka National Historical and Cultural Reserve}} [[Category:ಉಕ್ರೇನ್‌ದ ಅರಮನೆಲು]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:1770ರ ದಶಕೊದ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಉದ್ಯಾನೊಲು]] 5g6l17g7jtdpbkoi58vgo4x1qwf51ff 366153 366151 2026-08-21T08:22:09Z Hariprasad Shetty10 6127 ಪುಟದಲ್ಲಿರುವ ಎಲ್ಲಾ ಮಾಹಿತಿಯನ್ನೂ ತಗೆಯುತ್ತಿರುವೆ 366153 wikitext text/x-wiki phoiac9h4m842xq45sp7s6u21eteeq1 366159 366153 2026-08-21T09:08:18Z Hariprasad Shetty10 6127 366159 wikitext text/x-wiki {{Infobox | bodyclass = vcard | aboveclass = fn org | above = ಕೀವ್ ಕೋಟೆ | image = Kiev fortress 003 SHCH.jpg | caption = ಕೀವ್ ಕೋಟೆ | label1 = ಸ್ಥಳೀಯ ಹೆಸರು | data1 = Київська фортеця | label2 = ಸ್ಥಳ | data2 = ಹಾಸ್ಪಿಟಲ್ ರಸ್ತೆ 24A, ಕೀವ್, ಉಕ್ರೇನ್ | label3 = ಪ್ರಕಾರ | data3 = ಐತಿಹಾಸಿಕ ಕೋಟೆ ಬೊಕ್ಕ ಕೋಟೆ ಸಮುಚ್ಚಯ | label4 = ನಿರ್ಮಾಣ ಕಾಲ | data4 = 17ನೇ–19ನೇ ಶತಮಾನೊಲು | label5 = ಸಾರ್ವಜನಿಕ ಸಾರಿಗೆ | data5 = ಕ್ಲೋವ್ಸ್ಕಾ ಮೆಟ್ರೊ ನಿಲ್ದಾಣ; ಪಲಾತ್ಸ್ ಸ್ಪೋರ್ತು ಮೆಟ್ರೊ ನಿಲ್ದಾಣ; ಟ್ರಾಲಿಬಸ್ }} [[File:AFRE Kyev.jpg|thumb|ಕೀವ್ ಕೋಟೆದ 1830ರ ಯೋಜನೆ]] '''ಕೀವ್ ಕೋಟೆ''' ಕೀವ್‌ಡು ಇಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಕೋಟೆ ಸಮುಚ್ಚಯ. ಉಂದು 17ನೇ ಶತಮಾನೊಡ್ದ್ 19ನೇ ಶತಮಾನೊದ ತನಕ ಹಂತ ಹಂತವಾದ್ ನಿರ್ಮಾಣ ಆಯಿನ ಕೋಟೆಲು, ರಕ್ಷಣಾ ಗೋಡೆಲು, ದಂಡೆಲು, ಗೋಪುರೊಲು, ಬ್ಯಾರಕ್‌ಲು ಬೊಕ್ಕ ಬೇತೆ ಸೈನಿಕ ಕಟ್ಟಡೊಲೆನ ಸಮೂಹ ಆದುಂಡು.ಉಂದು ಕೀವ್ ಕೋಟೆದ ಕೆಲವು ಭಾಗೊಲೆನ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ ಭಾಗೊಲೆನಾದ್ ಸಂರಕ್ಷಣೆ ಮಲ್ದೆರ್.<ref name="IEU">{{cite web |title=Kyivan Cave Fortress |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CK%5CY%5CKyivanCaveFortress.htm |website=Internet Encyclopedia of Ukraine |publisher=Canadian Institute of Ukrainian Studies, University of Alberta |access-date=21 August 2026 }}</ref> ಕೋಟೆ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಭಾಗೊಲು ಕೀವ್‌ದ ಪೆಚೆರ್ಸ್ಕ್ ಪ್ರದೇಶೊಡು ಉಂಡು. ಇಂಚಿನ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ, ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ, ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ, ಲೈಸಾ ಹೊರಾ ಕೋಟೆ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರತ್ಯೇಕ ರಕ್ಷಣಾ ರಚನೆಲು ಸೇರುದುಂಡು.<ref name="IEU"/> ಉಂದು ಕೀವ್ ಕೋಟೆ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಸೈನಿಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ತಾಣೊಲೆಡ್ ಒಂಜಿ. ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಪ್ರವಾಸೋದ್ಯಮ ಮೂಲೊದ ಪ್ರಕಾರ, ಕೋಟೆದ ರಚನೆಲು ಸುಮಾರು 200 ವರ್ಷೊಲೆನ ಅವಧಿಡ್, 17ನೇ ಶತಮಾನೊಡ್ದ್ 19ನೇ ಶತಮಾನೊ ಮುಟ್ಟ ನಿರ್ಮಾಣ ಆತುಂಡ್.<ref name="KyivGuide">{{cite web |title=Kyiv Fortress |url=https://guide.kyivcity.gov.ua/en/places/kyyivska-fortetsya |website=Kyiv City Guide |publisher=Kyiv City Council |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಕೋಟೆ ವ್ಯವಸ್ಥೆ === ಕೀವ್ ನಗರೊಗು ಪುರಾತನ ಕಾಲೊಡ್ದೇ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆ ಇತ್ತ್ಂಡ್. ಕೀವನ್ ರಸ್ ಕಾಲೊಡು ನಗರದ ಮೇಲ್ಭಾಗೊಡು ಕೋಟೆಗೋಡೆಲು, ಮಣ್ಣಿನ ದಂಡೆಲು ಬೊಕ್ಕ ಕಂದಕೊಲು ನಿರ್ಮಾಣ ಆಯಿತ್ತ್ಂಡ್. 1240ಡ್ ಬತು ಖಾನ್ ನೇತೃತ್ವೊದ ಮಂಗೋಲ್ ಸೈನ್ಯ ಕೀವ್‌ನು ಮುತ್ತಿಗೆ ಹಾಕಿ ನಗರೊನು ನಾಶ ಮಲ್ತಿನ ಪಿರವುಟು ನಗರದ ಪರತ್ತ್ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆಗ್ ದೊಡ್ಡ ಹಾನಿ ಆಯಿನಿ.<ref name="KyivHistory">{{cite web |title=Kyiv |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CK%5CY%5CKyiv.htm |website=Internet Encyclopedia of Ukraine |publisher=Canadian Institute of Ukrainian Studies, University of Alberta |access-date=21 August 2026 }}</ref> ಪಿರಾವುಡು ಕೀವ್ ಲಿಥುವೇನಿಯನ್ ಬೊಕ್ಕ ಪೋಲಿಷ್-ಲಿಥುವೇನಿಯನ್ ಆಡಳಿತೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್. 17ನೇ ಶತಮಾನೊಡು ಕೀವ್‌ದ ರಾಜಕೀಯ ಬೊಕ್ಕ ಸೈನಿಕ ಮಹತ್ವೊ ಪಿರಾವುಡು ಹೆಚ್ಚಾಂಡ್.<ref name="KyivHistory"/> === 1654ರ ಪಿರ === 1654ರ ಪೆರಿಯಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ ಪಿರಾವುಡು ಕೀವ್‌ಡು ಮಸ್ಕೋವೈಟ್ ಸೈನಿಕ ಪಡೆದ ಶಿಬಿರೊ ಸ್ಥಾಪನೆ ಆದುಂಡು. ಈ ಕಾಲೊಡು ನಗರದ ಮೇಲ್ಭಾಗೊಡು ಒಂಜಿ ಪೊಸ ಸೈನಿಕ ಕೋಟೆ ನಿರ್ಮಾಣ ಆತುಂಡು.<ref name="KyivHistory"/> ಕೀವ್‌ಡು ಮಸ್ಕೋವೈಟ್ ಸೈನಿಕ ಉಪಸ್ಥಿತಿ ಹೆಚ್ಚಾಯಿನ ಜೊತೆಡು ಪೆಚೆರ್ಸ್ಕ್ ಪ್ರದೇಶೊದ ಸೈನಿಕ ಮಹತ್ವೊಲಾ ಹೆಚ್ಚಾಂಡ್. ಕುಡ ಈ ಪ್ರದೇಶೊಡು ಮಲ್ಲ ಕೋಟೆ ವ್ಯವಸ್ಥೆ ಅಭಿವೃದ್ಧಿ ಆಂಡ್.<ref name="RussianPresence">{{cite web |title=Russians in Ukraine |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CR%5CU%5CRussiansinUkraine.htm |website=Internet Encyclopedia of Ukraine |publisher=Canadian Institute of Ukrainian Studies, University of Alberta |access-date=21 August 2026 }}</ref> === 1679ರ ಕೋಟೆ ನಿರ್ಮಾಣ === 1679ಡ್ ಹೆಟ್ಮನ್ ಇವಾನ್ ಸಮೊಯ್ಲೊವಿಚ್ ಕಾಲೊಡು ಮೇಲಿನ ಮಠ ಪ್ರದೇಶೊದ ಸುತ್ತ ಪೊಸ ಕಣಿಕುಲು ಬೊಕ್ಕ ಮಣ್ಣಿನ ದಂಡೆಲು ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಕೆಲಸೊ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಅಭಿವೃದ್ಧಿಗ್ ಒಂಜಿ ಮುಖ್ಯ ಹಂತ ಆಂಡ್.<ref name="IEU"/> ಪಿರಾವುಡು ಹೆಟ್ಮನ್ ಇವಾನ್ ಮಜೆಪಾ ಕಾಲೊಡು ದಪ್ಪ ಕಲ್ಲಿನ ಗೋಡೆಲು ಬೊಕ್ಕ ನಾಲ್ಕು ಗೋಪುರೊಲು ಸೇರ್ಸಾಯಿನಿ. ಈ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆ ಕೀವ್ ಪೆಚೆರ್ಸ್ಕ್ ಪ್ರದೇಶೊನು ಹೆಚ್ಚು ಬಲಪಡಿಸಾಯಿನಿ.<ref name="IEU"/> == ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ದ ಕೋಟೆ == 1706ಡ್ ರಷ್ಯಾದ ಚಕ್ರವರ್ತಿ ಪೀಟರ್ ಪ್ರಥಮ ಕೀವ್‌ಡು ಹೊಸ ಸೈನಿಕ ಕೋಟೆ ನಿರ್ಮಾಣ ಮಲ್ಪುನ ಆದೇಶ ಕೊರಿಯೆರ್. ಈ ಕೋಟೆನು '''ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ''' ಅತ್ತ್ಂಡ '''ಓಲ್ಡ್ ಕೇವ್ ಕೋಟೆ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref name="IEU"/> ಕೋಟೆದ ಮೊದಲ ಯೋಜನೆ ಎಂಜಿನಿಯರ್ ಗೆಲ್ಲರ್ಟ್ ತಯಾರ್ ಮಲ್ತೆರ್. 1723ಡ್ ಕೋಟೆದ ಮುಖ್ಯ ನಿರ್ಮಾಣ ಪೂರ್ಣ ಆಂಡ್. ಉಂದು ಅರ್ಧವೃತ್ತಾಕಾರೊದ ಸಿಟಾಡೆಲ್, ಸುಮಾರು 6 ಮೀಟರ್ ಎತ್ತರದ ಮಣ್ಣಿನ ದಂಡೆಲು, ಎಂಟು ಬಲವರ್ಧಿತ ಭಾಗೊಲು ಬೊಕ್ಕ ಬೇತೆ ರಕ್ಷಣಾ ರಚನೆಲೆನ್ ಹೊಂದಿತ್ತ್ಂಡ್.<ref name="IEU"/> ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಮೂಲ ಮೂಜಿ ಮುಖ್ಯ ಬಾಗಿಲೊಲೆಡ್ ವಾಸಿಲ್ಕಿವ್ ಬೊಕ್ಕ ಮಾಸ್ಕೋ ಬಾಕಿಲುಲು ಪಿರಾವುಡು ಒರಿದಿತ್ತ್ಂಡ್. ಕೋಟೆ ರಷ್ಯನ್ ಸಾಮ್ರಾಜ್ಯೊದ ಕೀವ್‌ದ ಸೈನಿಕ ಬೊಕ್ಕ ಆಡಳಿತಾತ್ಮಕ ಕೇಂದ್ರೊಲೆಡ್ ಒಂಜಿ ಆತ ಬಳಕೆ ಆಯಿನಿ.<ref name="IEU"/> [[File:Kyiv Arsenal panorama.jpg|thumb|ಕೀವ್ ಆರ್ಸೆನಲ್]] == ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ == 1831ಡ್ದ್ 1863ರ ತನಕ್ ಕೀವ್‌ಡು '''ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ''' ನಿರ್ಮಾಣ ಆಯಿನಿ. ಈ ಕೋಟೆದ ನಿರ್ಮಾಣೊ ಎಂಜಿನಿಯರ್ ಒಪ್ಪರ್ಮನ್‌ದ ಮಾರ್ಗದರ್ಶನೊಡು ನಡತ್ಂಡ್.<ref name="IEU"/> ಪೊಸ ಕೋಟೆ ವ್ಯವಸ್ಥೆಡು ಪೊಸ ಮಣ್ಣುದ ದಂಡೆಲು, ಬ್ಯಾರಕ್‌ಲು, ರಕ್ಷಣಾ ಗೋಡೆಲು ಬೊಕ್ಕ ಬೇತೆ ಸೈನಿಕ ಕಟ್ಟಡೊಲು ನಿರ್ಮಾಣ ಆಂಡ್. ಕೆಲವು ಪೊಸ ದಂಡೆಲು 15 ಮೀಟರ್ ತನಕ ಎತ್ತರ ಇತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಒಂಜಿ ಪ್ರಮುಖ ದಂಡೆದ ಉದ್ದ ಸುಮಾರು 1,039 ಮೀಟರ್ ಇತ್ತ್ಂಡ್.<ref name="IEU"/> ಪೊಸ ಕೋಟೆದ ಮುಖ್ಯ ಭಾಗೊಲೆಡ್ '''ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ''' ಬೊಕ್ಕ '''ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ''' ಪ್ರಮುಖ ಆದುಂಡು. ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆಡು ಸೈನಿಕ ಆಸ್ಪತ್ರೆಲಾ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="IEU"/> == ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ == '''ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ''' (Hospital Fortification) ಕೀವ್ ಕೋಟೆದ ಪ್ರಮುಖ ಭಾಗೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ನಿರ್ಮಾಣೊದ ಕಾಲೊಡು ಅಭಿವೃದ್ಧಿ ಆತುಂಡು. ಇಂಚ ಸೈನಿಕ ಆಸ್ಪತ್ರೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ಕಟ್ಟಡೊಲು ಇತ್ತ್ಂಡ್.<ref name="IEU"/> ಉಂದು ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಪ್ರದೇಶೊಡು ಕೋಟೆಗೋಡೆ, ರಕ್ಷಣಾ ದಂಡೆಲು, ಕ್ಯಾಪೊನಿಯರ್‌ಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಉಳಿದ್ಂಡ್. ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ''ಕೀವ್ ಕೋಟೆ''ದ ಪ್ರಮುಖ ಭಾಗೊ ಇಂಚೆಡ್ ಕಾರ್ಯ ನಿರ್ವಹಿಸುಂಡು.<ref name="KyivMuseum">{{cite web |title=Національний історико-архітектурний музей «Київська фортеця» |url=https://dk.kyivcity.gov.ua/muzei/natsionalnyi-istoryko-arkhitekturnyi-muzei-kyivska-fortetsia |website=Department of Culture, Kyiv City Council |date=24 April 2024 |access-date=21 August 2026 }}</ref> [[File:Северная полубашня Госпитального замка.JPG|thumb|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಉತ್ತರ ಅರೆ-ಗೋಪುರ]] == ಕ್ಯಾಪೊನಿಯರ್ == ಕೀವ್ ಕೋಟೆದ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆಡು ಕ್ಯಾಪೊನಿಯರ್‌ಲು ಪ್ರಮುಖ ರಚನೆಲು ಆತುಂಡು. ಕ್ಯಾಪೊನಿಯರ್ ಪಂಡ ಕೋಟೆದ ಕಂದಕ ಬೊಕ್ಕ ಗೋಡೆದ ರಕ್ಷಣೆದ ಬಳಸುನ ಸೈನಿಕ ರಚನೆ. '''ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್''' (Kosyi Kaponir) ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಒಂಜಿ ಪ್ರಸಿದ್ಧ ರಕ್ಷಣಾ ರಚನೆ. ಉಂದು ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ ಭಾಗ ಆದುಂಡು. ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಮೂಲೊದ ಪ್ರಕಾರ, ಕೀವ್ ಕೋಟೆದ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಸಮುಚ್ಚಯೊಡು ರಕ್ಷಣಾ ದಂಡೆಲು, ಕೋಟೆಗೋಡೆ ಬೊಕ್ಕ ಕ್ಯಾಪೊನಿಯರ್‌ಲೆನ ಬಯಲು ಪ್ರದರ್ಶನೊ ಉಂಡು.<ref name="KyivGuide"/> [[File:Kiev-FortificationKosoyKaponir 05.jpg|thumb|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಕ್ಯಾಪೊನಿಯರ್‌ಡು ಫಿರಂಗಿಲು]] == ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ == '''ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ''' ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಒಂಜಿ ಮುಖ್ಯ ಭಾಗ. ಉಂದು ವಾಸಿಲ್ಕಿವ್ ದಿಕ್ಕ್‌ದ್ ಕೀವ್‌ಗು ಬರ್ಪುನ ಮಾರ್ಗೊನು ರಕ್ಷಣೆ ಮಲ್ಪುನ ಉದ್ದೇಶೊಡು ನಿರ್ಮಾಣ ಮಲ್ದಿನಿ.<ref name="IEU"/> ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆದ ಒರಿದಿನ ರಚನೆಲೆಡ್ ಕೆಲವು ಗೋಪುರೊಲು ಪ್ರಮುಖ ಆದುಂಡು. ಅವುಲೆಡ್ ಗೋಪುರ ಸಂಖ್ಯೆ 1ರ ರೆಡ್ಯೂಯಿಟ್, ವೃತ್ತಾಕಾರ ಗೋಪುರ ಸಂಖ್ಯೆ 2 ಬೊಕ್ಕ ಪ್ರೊಜೊರಿವ್ಸ್ಕಾ ಗೋಪುರ ಸಂಖ್ಯೆ 3 ಸೇರ್ದ್ ಉಂಡು. == ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ == '''ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ''' ಕೀವ್‌ದ ಜ್ವಿರಿನೆಟ್ಸ್ ಪ್ರದೇಶೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಬೊಕ್ಕೊಂಜಿ ರಕ್ಷಣಾ ಸಮುಚ್ಚಯ ಆಂಡ್. ಉಂದು 19ನೇ ಶತಮಾನೊದ ಕೀವ್ ಕೋಟೆದ ವಿಸ್ತೃತ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆದ ಭಾಗ ಆದ್ ಬೆಳೆದುಂಡು. ಜ್ವಿರಿನೆಟ್ಸ್ ಪ್ರದೇಶೊಡು ಪಿರಾವುಡು ನಿರ್ಮಾಣ ಆಯಿನ ಸೈನಿಕ ರಚನೆಲೆನ ಕೆಲವು ಅವಶೇಷೊಲು ಇಂಚ ತೋಜುದು ಬರ್ಪುಂಡು. == ಲೈಸಾ ಹೊರಾ ಕೋಟೆ == '''ಲೈಸಾ ಹೊರಾ''' ("ಬೊಳು ಗುಡ್ಡೆ") ಕೀವ್‌ದ ದಕ್ಷಿಣ ಭಾಗೊಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಕೋಟೆ ಪ್ರದೇಶ ಆಂಡ್. 19ನೇ ಶತಮಾನೊದ ಕೊನೆ ಭಾಗೊಡು ಇಂಚ ಕೋಟೆ ರಚನೆಲು ನಿರ್ಮಾಣ ಆಯಿನಾವುಲು. ಪಿರವುಡು ಲೈಸಾ ಪಿದಯಿದ ರಾಜಕೀಯ ಕೈದಿಲೆನ ಮರಣದಂಡನೆದ ಜಾಗೆ ಆತ್ ಬಳಕೆ ಆತುಂಡು. ಉಂದು ಈ ಪ್ರದೇಶೊನು ಭೂದೃಶ್ಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ತಾಣೊ ಆದ್ ಪರಿಗಣಿಸಾವುದುಂಡು. == ಕೋಟೆದ ರಚನೆ == ಕೀವ್ ಕೋಟೆ ಒಂಜಿ ಏಕೈಕ ಕಟ್ಟಡ ಅತ್ತು. ಉಂದು ಮಸ್ತ್ ಪ್ರತ್ಯೇಕ ರಕ್ಷಣಾ ಸಮುಚ್ಚಯೊಲೆನ ಒಟ್ಟುಗೂಡಿಕೆ ಆತುಂಡು. ಮುಖ್ಯ ಭಾಗೊಲೆಡ್: * ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ * ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ * ಸಿಟಾಡೆಲ್ * ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ * ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ * ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ * ಲೈಸಾ ಹೊರಾ ಕೋಟೆ * ಕ್ಯಾಪೊನಿಯರ್‌ಲು * ಕೋಟೆಗೋಡೆ ಬೊಕ್ಕ ಮಣ್ಣಿನ ದಂಡೆಲು * ಸೈನಿಕ ಬ್ಯಾರಕ್‌ಲು * ಗೋಪುರೊಲು ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಉಳಿದಿನ ಪ್ರಮುಖ ರಚನೆಲೆಡ್ ಮಿಕಿಲ್ಸ್ಕಾ ಬಾಕಿಲು, ವೃತ್ತಾಕಾರ ಗೋಪುರ ಬೊಕ್ಕ ಪ್ರೊಜೊರಿವ್ಸ್ಕಾ ಗೋಪುರ ಸೇರ್ದ್ ಉಂಡು.<ref name="IEU"/> == ಸೈನಿಕ ಮಹತ್ವ == 18ನೇ ಬೊಕ್ಕ 19ನೇ ಶತಮಾನೊಡು ಕೀವ್ ಕೋಟೆ ರಷ್ಯನ್ ಸಾಮ್ರಾಜ್ಯೊದ ಪ್ರಮುಖ ಸೈನಿಕ ಬೊಕ್ಕ ಆಡಳಿತಾತ್ಮಕ ಕೇಂದ್ರೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಅಭಿವೃದ್ಧಿ ಕೀವ್‌ಡು ರಷ್ಯನ್ ಸೈನಿಕ ಉಪಸ್ಥಿತಿ ಹೆಚ್ಚಾಯಿನ ಜೊತೆಡು ನಡತ್ಂಡ್.<ref name="RussianPresence"/> 19ನೇ ಶತಮಾನೊಡು ಆಧುನಿಕ ತೋಪು ಬೊಕ್ಕ ಪೊಸ ಸೈನಿಕ ತಂತ್ರಜ್ಞಾನೊಲೆನ ಅಭಿವೃದ್ಧಿ ಆಯಿನ ಕಾರಣೊಡು ಪುರಾತನ ಕೋಟೆದ ನೇರ ಸೈನಿಕ ಮಹತ್ವೊ ಕಡಿಮೆ ಆತುಂಡು. ಪಿರಾವುಡು ಕೋಟೆದ ಮಸ್ತ್ ಕಟ್ಟಡೊಲೆನ್ ಸೈನಿಕ ಗೋದಾಮು, ಬ್ಯಾರಕ್ ಬೊಕ್ಕ ಜೈಲು ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್.<ref name="IEU"/> == 20ನೇ ಶತಮಾನ == 20ನೇ ಶತಮಾನೊಡು ಕೀವ್ ಕೋಟೆದ ಕೆಲವು ಕಟ್ಟಡೊಲು ಸೈನಿಕ, ಆಡಳಿತಾತ್ಮಕ, ಗೋದಾಮು ಬೊಕ್ಕ ಜೈಲು ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಆದುಂಡು. ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್ ರಾಜಕೀಯ ಕೈದಿಲೆನ ಜೈಲು ಆದ್ ಬಳಕೆ ಆತುಂಡು. ಪಿರವುಡು ಕೋಟೆದ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಮಹತ್ವೊದ ಗುರುತಿಸಾಯಿನ ಸಂರಕ್ಷಣೆಗ್ ಒತ್ತು ಕೊರಿಯೆರ್. 1930ಡ್ ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್ ಮ್ಯೂಸಿಯಂ ಸ್ಥಾಪನೆ ಆತಿನಿ. 1991ಡ್ ಉಂದು ''ಕೀವ್ ಕೋಟೆ'' ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ಸ್ಮಾರಕ-ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಆತ್ ಮರುನಾಮಕರಣ ಆಂಡ್. 2007ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊದ ಮೂಲಕ ಮ್ಯೂಸಿಯಂ‌ಗು ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="KyivMuseum"/> == ಕೀವ್ ಕೋಟೆದ ವಸ್ತುಸಂಗ್ರಹಾಲಯ == '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯ "ಕೀವ್ ಕೋಟೆ"''' ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಆಧಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಒಂಜಿ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಸಮುಚ್ಚಯ ಆಂಡ್. ಇಂಚ ಕೀವ್‌ದ ಕೋಟೆ ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸೈನಿಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಇತಿಹಾಸೊನು ತೋಜಾವುನ ಪ್ರದರ್ಶನೊಲು ಉಂಡು.<ref name="KyivMuseum"/> ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಮೂಲೊದ ಪ್ರಕಾರ, ಮ್ಯೂಸಿಯಂ ಸಮುಚ್ಚಯೊಡು ಕೀವ್ ಕೋಟೆದ ಕಟ್ಟಡೊಲೆನ ಬಳಕೆದ ಇತಿಹಾಸೊ, ಕೋಟೆ ನಿರ್ಮಾಣೊದ ಇತಿಹಾಸೊ, ತೋಪು ಬೊಕ್ಕ ರಕ್ಷಣಾ ತಂತ್ರಜ್ಞಾನೊ, ಬೊಕ್ಕ ಬಯಲು ಪ್ರದರ್ಶನೊ ಸೇರ್ದ್ ಉಂಡು.<ref name="KyivGuide"/> ಮ್ಯೂಸಿಯಂ‌ಗು 2007ಡ್ ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="KyivMuseum"/> == ಇತ್ತೆದ ಸ್ಥಿತಿ == ಇತ್ತೆ ಕೀವ್ ಕೋಟೆದ ಮಸ್ತ್ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು. ಕೆಲವು ರಚನೆಲು ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ ಭಾಗ ಆದುಂಡು, ಬಾಕಿ ಕೆಲವು ಕಟ್ಟಡೊಲು ಬೇತೆ ಬಳಕೆಲೆಗ್ ಒಳಪಟ್ಟುದುಂಡು. ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಪ್ರವಾಸಿ ಮಾರ್ಗದರ್ಶಿ ಕೀವ್ ಕೋಟೆನು ವಿಶ್ವೊಡು ಒರಿದಿನ ಮಲ್ಲ ಕೋಟೆ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಪಂಡ್ದ್ ವಿವರಿಸಾವೆರ್. ಇಂಚ ಕೋಟೆದ ಗೋಡೆ, ರಕ್ಷಣಾ ದಂಡೆಲು, ಕ್ಯಾಪೊನಿಯರ್‌ಲು ಬೊಕ್ಕ ಲೈಸಾ ಹೊರಾ ಕೋಟೆದ ಬಯಲು ಪ್ರದರ್ಶನೊಲಾ ಉಂಡು.<ref name="KyivGuide"/> == ಕೋಟೆಡ್ ಸೇವೆ ಮಲ್ತಿನ ಗಮನಾರ್ಹ ವ್ಯಕ್ತಿಲು == * ಕಾನ್ಸ್ಟಾಂಟೈನ್ ಯಿಪ್ಸಿಲಾಂಟಿಸ್ – 1807 ಬೊಕ್ಕ 1816 ನಡುಟು ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಕಮಾಂಡೆಂಟ್ ಆದ್ ಸೇವೆ ಮಲ್ತೆರ್. == ಚಿತ್ರೊಲೆನ್ ಗ್ಯಾಲರಿ == <gallery widths="180"> File:Kiev-Fortress22-03-2011.jpg|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಮುಖ್ಯ ಬಾಗಿಲು ಬೊಕ್ಕ ಗೋಡೆ — ಪೂರ್ವೊದ ಕಡೆಡ್ದ್ File:Hospital fort of the Kiev fortress.jpg|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಮುಖ್ಯ ಬಾಗಿಲು ಬೊಕ್ಕ ಗೋಡೆ — ಪಶ್ಚಿಮೊದ ಕಡೆಡ್ದ್ File:Kosoj Caponier of the Kiev fortress.jpg|ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್ File:Башня №1 (редюит) Васильковского укрепления.JPG|ಗೋಪುರ ಸಂಖ್ಯೆ ೧ — ರೆಡ್ಯೂಯಿಟ್, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ File:Круглая башня (№ 2) Васильковского укрепления.JPG|ವೃತ್ತಾಕಾರ ಗೋಪುರ ಸಂಖ್ಯೆ ೨, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ File:Башня № 3 (Прозоровская).JPG|ಪ್ರೊಜೊರಿವ್ಸ್ಕಾ ಗೋಪುರ ಸಂಖ್ಯೆ ೩, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ </gallery> == ಇಂಚಲಾ ತೂಲೆ == * ಕ್ಲೋವ್ ಅರಮನೆ * ಕೀವ್ ಪೆಚೆರ್ಸ್ಕ್ ಲಾವ್ರಾ * ಕೀವ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲು == * [https://www.kyiv-fortress.org.ua/ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯ "ಕೀವ್ ಕೋಟೆ" — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kyiv Fortress}} [[Category:ಕೀವ್‌ದ ಕಟ್ಟಡೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಕೋಟೆಲು]] [[Category:ಕೀವ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಸೈನಿಕ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲು]] [[Category:ಕೀವ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] 9lv3d5v3zeuv36rs1ae1ivapgcot8zq 366161 366159 2026-08-21T09:09:17Z Hariprasad Shetty10 6127 ಪುಟದಲ್ಲಿರುವ ಎಲ್ಲಾ ಮಾಹಿತಿಯನ್ನೂ ತಗೆಯುತ್ತಿರುವೆ 366161 wikitext text/x-wiki phoiac9h4m842xq45sp7s6u21eteeq1 366175 366161 2026-08-21T10:01:19Z Hariprasad Shetty10 6127 366175 wikitext text/x-wiki {{Short description|ಕೀವ್, ಉಕ್ರೇನ್‌ಡ್ ಇಪ್ಪುನ ಆರ್ಟ್ ನುವೊ ವಾಸ್ತುಶಿಲ್ಪೊದ ಕಟ್ಟಡ}} {{Infobox building | name = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್ | native_name = Будинок з химерами | native_name_lang = uk | image = Будинок із химерами-3.JPG | image_alt = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ದೃಶ್ಯ | image_caption = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ಮುಖಭಾಗ | architectural_style = ಆರ್ಟ್ ನುವೊ | structural_system = ಕಾಂಕ್ರೀಟ್ ಪೈಲುಲು; ನಿರಂತರ ಅಡಿಪಾಯ | location = ಲಿಪ್ಕಿ, ಕೀವ್, ಉಕ್ರೇನ್ | address = 10, ಬ್ಯಾಂಕೋವಾ ಬೀದಿ | coordinates = {{coord|50|26|42|N|30|31|43|E|type:landmark_region:UA-30|display=inline,title}} | construction_start_date = 1901 | completion_date = 1902 | floor_count = 3 (ಬ್ಯಾಂಕೋವಾ ಬೀದಿ ಬದಿಟ್)<br>6 (ಫ್ರಾಂಕೊ ಚೌಕ ಬದಿಟ್) | floor_area = {{convert|3309.5|m2|sqft|2|abbr=on}} | architect = ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರಡೆಟ್ಸ್ಕಿ }} '''ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್''' ({{langx|uk|Budinok з khimerami|Budynok z khymeramy}}) ಅತ್ತಂದೆ '''ಹೊರೊಡೆಟ್ಸ್ಕಿ ಹೌಸ್''' ([[Władyslaw Horodecki]] ಪುದರ್ ಡ್ ಪುದರ್ ದೀತಿನ) [[ಆರ್ಟ್ ನೌವಿಯೊ]] ಕಟ್ಟಡ ಉಂದು [[Subdivisions of KyivH#Ihistorical| [[ಲಿಪ್ಕಿ]] [[ಕಿವ್]] ದ ನೆರೆಹೊರೆ, ಉಕ್ರೇನ್ ದೇಶದ ರಾಜಧಾನಿ. [[ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆ]]ನ ಕಚೇರಿದ ಬೀದಿದ ಎದುರುಡು ಉಪ್ಪುನ ಈ ಕಟ್ಟಡೊನು 2005 ಡ್ದ್ ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಅಧ್ಯಕ್ಷೆರೆನ ನಿವಾಸೊ ಆದ್ ಬಳಕೆ ಮಲ್ತೊಂದು ಬೈದೆರ್.<ref name="vr-340">{{Cite Ukrainian law|type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=340|law=340-2005-п|name=On the addition to the house on 10, Bankova St. in the city of Kyiv the status of a government residence|date=2005-05-11}}</ref><ref name="BBC Ukrainian">{{cite news|url=https://www.bbc.com/ukrainian/indepth/story/2005/04/050401_ukraine_residences |title=The aura is fine, but the place is too small|access-date=2006-09-30|first=Marta|last=Shokalo|date=2005-04-01|publisher=[[BBC News|BBC Ukrainian]]|language=uk}}</ref> ಕಟ್ಟಡೊದ ಎದುರುದ ಬೀದಿನ್ ಮಾತಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ಗ್ ಮುಚ್ಚಿದ್ ದೀತೆರ್, ಬೊಕ್ಕ ಇತ್ತೆ [[ಅಧ್ಯಕ್ಷೀಯ ಕಚೇರಿ ಕಟ್ಟಡ (ಕೀವ್)|ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡ]] ಗ್ ಸಮೀಪೊಡು ಉಪ್ಪುನೆರ್ದಾತ್ರ ಉಂದು ಒಂಜಿ ಗಸ್ತು [[ಪಾದಚಾರಿ ವಲಯ]] ಆದುಂಡು. ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ಮೂಲತಃ 1901-1902 ಡ್ ತನ್ನ ಸ್ವಂತ ಅಪ್ಮಾರ್ಕೆಟ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಕಟ್ಟಡೊ ಆದ್ ಬಳಕೆ ಮಲ್ಪುನ ನಿಟ್ಟಿಡ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಆಂಡಲಾ, ವರ್ಷೊಲು ಕರಿಲೆಕನೆ, ಹೊರೊಡೆಕಿ ಕಡೆಕ್ ಆರ್ಥಿಕ ತೊಂದರೆಲೆಡ್ದ್ ಕಟ್ಟಡೊನು ಮಾರಾಟ ಮಲ್ಪೊಡಾಂಡ್, ಅಯಿಡ್ದ್ ಬೊಕ್ಕ 2000 ದ ಸುರುತ ಮುಟ್ಟ ಉಕ್ರೇನ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಅಧಿಕೃತ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಕ್ರಮಣ ಮಲ್ಪುನ ದುಂಬು ಉಂದು ಅನೇಕ ಸರ್ತಿ ಮಾಲೀಕತ್ವೊನು ಬದಲಾವಣೆ ಮಲ್ತ್ಂಡ್.<ref name="riddle"/> ಕಟ್ಟಡೊನು ಖಾಲಿ ಮಲ್ತಿನಗ, ಅಯಿತ ಒಳಾಂಗಣ ಬೊಕ್ಕ ಪಿದಯಿದ ಅಲಂಕಾರೊನು ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆನ್ ಅನುಸರಿಸಾದ್ ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆ ಮಲ್ತೆರ್.<ref name="mirtu"/> ಹೊರೊಡೆಕಿ ಒರಿ ಉತ್ಸಾಹಿ ಬೇಟೆಗಾರೆ ಆಯಿನೆಡ್ದಾವರ, ಇಟಾಲಿಯನ್ ವಾಸ್ತುಶಿಲ್ಪಿ [[ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ)|ಎಮಿಲಿಯೊ ಸಾಲಾ]] ಶಿಲ್ಪಕಲೆ ಮಲ್ತಿನ ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲೆನ್ ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲೆನ್ ಚಿತ್ರಿಸವುನ ಅಲಂಕಾರಿಕ ಅಲಂಕಾರೊಲೆಡ್ದ್ ಈ ಕಟ್ಟಡೊಗು ಜನಪ್ರಿಯ ಪುದರ್ ಬತ್ತ್ಂಡ್.<ref name="vinnytsia_library"/> ಈ ಪುದರ್ ಪುರಾಣೊದ [[ಚಿಮೆರಾ (ಪುರಾಣ)|ಚಿಮೆರಾ]] ನ್ ಸೂಚಿಸವುಜಿ, ಆಂಡ [[ಚಿಮೆರಾ (ವಾಸ್ತುಶಿಲ್ಪ)|ಚಿಮೇರಾ]] ಪನ್ಪಿನ ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿನ್ ಸೂಚಿಸವುಂಡು, ಉಂದೆಟ್ ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಒಂಜಿ ಕಟ್ಟಡೊಗು ಅಲಂಕಾರಿಕ ಅಂಶೊಲೆನ ರೂಪೊಡು ಅನ್ವಯಿಸವೆರ್. ಹೊರೊಡೆಕಿನ ಅನನ್ಯ ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿ ಕೀವ್ ದ [[ಆಂಟೋನಿ ಗೌಡಿ]] ಪಂಡ್ದ್ ಮೆಚ್ಚುಗೆನ್ ಗಳಸೊಂಡು.<ref name="mirtu">{{harvnb|Ivanenko|2004}}</ref><ref name="element">{{cite news|url=http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |title=heed the krai |access-date=2006-09-30 |first=Sonya |last=Rinkus |work=elementmoscow.ru |url-status=dead |archive-url=https://web.archive.org/web/20071109061542/http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |archive-date=2007-11-09 }}</ref> ==ಇತಿಹಾಸ== ===ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಸುರುತ ಇತಿಹಾಸ=== ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಒಂಜಿ ಇಲ್ಲ್ ನ್ ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ [[ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ]] ೧೯೦೧-೧೯೦೨ಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್.<ref name="mirtu"/> ಹೊರೊಡೆಕಿ ೧೮೬೩ಡ್ [[ಪೊಡೊಲಿಯಾ|ಪೊಡಿಲಿಯಾ]] ಪ್ರದೇಶೊಡು ಒಂಜಿ ಸಮೃದ್ಧ [[ಪೋಲಿಷ್ ಜನಕುಲು|ಪೋಲಿಷ್]] [[szlachta]] (ಉತ್ತಮ) ಕುಟುಂಬೊಡು ಪುಟ್ಟುವೆರ್.<ref name="vinnytsia_library">{{cite web|url=http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |title=Architector of century |date=2003-05-20 |access-date=2007-07-14 |publisher=Vinnytsia oblast' universal science library named after K.A.Timiryazev |language=uk |archive-url=https://web.archive.org/web/20070928062123/http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |archive-date=September 28, 2007 |url-status=dead }}</ref> ೧೮೯೦ಡ್ [[ಇಂಪೀರಿಯಲ್ ಅಕಾಡೆಮಿ ಆಫ್ ಆರ್ಟ್ಸ್]] ನ್ [[ಸೈಂಟ್ ಪೀಟರ್ಸ್ಬರ್ಗ್]] ಮುಗಿತ್ ಬೊಕ್ಕ, ಆರ್ ಕೀವ್ ಗ್ ಪೋಯೆರ್, ಅಲ್ಪ ಆರ್ ಸುಮಾರ್ ೩೦ ವರ್ಸೊಗು ಜೀವನ ಮಲ್ತೆರ್.<ref name="vinnytsia_library"/> ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಹೊರೊಡೆಕಿ ಒರಿ ಪ್ರಮುಖ ಕೀವ್ ವಾಸ್ತುಶಿಲ್ಪಿಯಾದ್ ತನ್ನನ್ ತಾನ್ ಸ್ಥಾಪಿಸೊಂದಿತ್ತೆರ್, ಅರೆನ ನಿಕಟ ಸ್ನೇಹಿತೆ ಬೊಕ್ಕ ಪಾಲುದಾರ ಎಂಜಿನಿಯರ್ [[ಆಂಟನ್ ಸ್ಟ್ರಾಸ್]] ನೊಟ್ಟುಗು ಮಸ್ತ್ ನಗರ ಕಟ್ಟಡೊಲೆನ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್, [[ಸೆಂಟ್. ನಿಕೋಲಸ್ ರೋಮನ್ ಕ್ಯಾಥೊಲಿಕ್ ಕ್ಯಾಥೆಡ್ರಲ್, ಕೀವ್|ಸೇಂಟ್. ನಿಕೋಲಸ್ ರೋಮನ್ ಕ್ಯಾಥೊಲಿಕ್ ಕ್ಯಾಥೆಡ್ರಲ್]] ಡ್ದ್ [[ಕರೈಟೆ ಕೆನೆಸಾ (ಕೀವ್)|ಕರೈಟೆ ಕೆನೆಸಾ]] ಬೊಕ್ಕ ಇನಿ [[ಉಕ್ರೇನ್ ದ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ಮ್ಯೂಸಿಯಂ]]. ವಾಸ್ತುಶಿಲ್ಪೊದ ಜೊತೆಗೆ, ಹೊರೊಡೆಕಿಗ್ [[ಮಲ್ಲ-ಆಟದ ಬೇಟೆಗಾರ|ಮಲ್ಲ-ಆಟದ ಬೇಟೆ]] ಡ್ ಆಸಕ್ತಿ ಇತ್ತ್ಂಡ್,<ref name="vinnytsia_library"/> ಉಂದು ಅರೆನ ಕಟ್ಟಡೊಡು ದಾಯೆಗ್ ಮಸ್ತ್ ಪ್ರಾಣಿಲೆನ್ ತೋಜಾವುಂಡು ಪನ್ಪಿನೆನ್ ವಿವರಿಸವುಂಡು. [[File:House with Chimaeras during construction.jpg|thumb|left|ನಿರ್ಮಾಣೊಡು ಉಪ್ಪುನ ಕಟ್ಟಡ, ೧೯೦೨]] ಹೊರೊಡೆಕಿ ಸಾಲದ ದುಡ್ಡುಡ್ದ್ ಇಲ್ಲ್ ದ ನಿರ್ಮಾಣೊಗು ಹಣಕಾಸು ಮಲ್ತೆರ್,<ref name="mirtu"/><ref name="KV"/> ಉಂದು ಒಂಜಿ [[ಅಪಾರ್ಟ್ಮೆಂಟ್ ಕಟ್ಟಡ]] ಆಯೆರೆ ಉದ್ದೇಶೊಡು.<ref name="Horodetskyi">{{harvnb|Malakov|1999|p=?}}</ref><ref name="Touring Kyiv">{{harvnb|Malikenaite|2003|p=50.}}</ref> ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಲಾ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ರೂಪಿಸೊಂದಿತ್ತ್ಂಡ್, ಉಂದೆನ್ [[ಎಲಿವೇಟರ್]] ಬೊಕ್ಕ ಮೆಟ್ಟಿಲುಲೆಡ್ದ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್.{{refn|ಉಂದೆಕ್ ಒಂಜೇ ಅಪವಾದ ಪಂಡ ಕಟ್ಟಡೊದ ಕೆಳಮಹಡಿಡ್ ರಡ್ಡ್ ಬೇತೆ ಬೇತೆ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್.|group=nb}} ಹೊರೊಡೆಕಿ ಸ್ವತಃ ಕಟ್ಟಡದ ನಾಲ್ನೇ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ತೆರ್, ಸುಮಾರ್{{convert|380|m2|sqft|abbr=on}}.<ref name="Horodetskyi"/>{{refn|ಅರೆನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಸೂಕ್ತವಾಯಿನವು. ನಂ. 3, ಮುಖ್ಯ ಮಹಡಿ, ಬಂಕೋವಾ ಬೀದಿದ ಮಟ್ಟೊಡ್ದ್ ತೂಂಡ.|group=nb}} ಹೊರೊಡೆಕಿ ಫೆಬ್ರವರಿ ೧, ೧೯೦೧ಡ್ ಸುರುತ ಜಮಿನ್ ನ್ ಕೊಂಡೊಂತೆರ್, ಆ ವರ್ಷದ ಮಾರ್ಚ್ ೧೮ಡ್ ನಿರ್ಮಾಣದ ಬೇಲೆ ಸುರು ಆಂಡ್. ಪಿದಯಿದ ಗೋಡೆಲೆನ ನಿರ್ಮಾಣ ಆಗಸ್ಟ್ ೨೧ ಗ್ ಮುಗಿಂಡ್, ಬೊಕ್ಕ ಛಾವಣಿನ್ ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಮಾತಾ [[ಕಲ್ಲುದ]] ಬೇಲೆನ್ ಸೆಪ್ಟೆಂಬರ್ ೧೩ ಗ್ ಮುಗಿತ್ಂಡ್.<ref name="Horodetskyi"/><ref name="Touring Kyiv"/> [[ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯ]] ದ ಒಳಗೆ ಆರ್ಥಿಕ ಕಷ್ಟೊಲೆಡ್ದ್, ಕಟ್ಟಡೊದ ಪೂರ್ಣಗೊಳಿಸುವಿಕೆ ತಡವಾಂಡ್. ಮೇ ೧೯೦೩ಡ್, ಕೆಳಮಟ್ಟೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಬೊಕ್ಕ ಹೊರೊಡೆಕಿನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಮಾತ್ರ ಆಕ್ರಮಣ ಮಲ್ತೆರ್.<ref name="Horodetskyi"/> ಭೂಮಿ ಬೊಕ್ಕ ನಿರ್ಮಾಣದ ಒಟ್ಟು ವೆಚ್ಚ 133,000 ಆಂಡ್ [[ರಷ್ಯನ್ ರೂಬಲ್#ಪ್ರಥಮ ರೂಬಲ್, ಪ್ರಾಚೀನತೆ - ಡಿಸೆಂಬರ್ 31, 1921|ರೂಬಲ್ಸ್]].{{refn|ಈ ಬೆಲೆ ೧೯೦೩ ಡ್ದ್ ಉಂಡು.[https://web.archive.org/web/20170323182358/http://www.archives.gov.ua/Archives/index.php?ra27 Government Archive of the City of Kyiv] – [[Fonds|Fond]] No. 143, Series 2, File No. 520, Item No. 9. In the late 19th and early 20th century, one thousand [[Russian ruble]]s were worth about 24.89 [[gold]] [[troy ounce]]s, or roughly one million Russian rubles as of 2011.{{sfn|Malakov|1999}}|group=nb}} ಒಟ್ಟುಗು, {{convert|1550|m2|sqft|abbr=on}} ಜಮಿನ್ ನ್ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಗು ಬಳಕೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಒಟ್ಟು 15,640 ರೂಬಲ್ಸ್ ಖರ್ಚು ಆಂಡ್.<ref name="KV">{{cite web|url=http://old.kv.com.ua/index.php?rub=46&number_old=2984|title=The rebirth of the House with Chimaeras|access-date=2006-07-14|first=Mikhail|last=Kalnitsky|work=Kievskie Vedomosti №179 (2984)|language=ru|date=2003-08-19|archive-date=2018-08-20|archive-url=https://web.archive.org/web/20180820045803/http://old.kv.com.ua/index.php?rub=46&number_old=2984|url-status=dead}}</ref> ಬಾಡಿಗೆಡ್ದ್ ವಾರ್ಷಿಕ ಲಾಭ 7,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್. ಹೊರೊಡೆಕಿ ತಾಜಾ ಇನ್-ಹೌಸ್ ಹಾಲುದ ಒತ್ತಾಯೊಗು ಕಾರಣವಾದ್ ಒಂಜಿ ಗೋಕುಲೊನು ಆವರಣೊಡು ದೀಯೆರ್,<ref name="mirtu"/><ref name="Touring Kyiv"/> ಗಾಯೊಲೆನ ವಾಸನೆ ಬಾಡಿಗೆದಾರೆರೆಗ್ ತೊಂದರೆ ಆವಂದಿಲೆಕ ನಿರ್ದಿಷ್ಟವಾದ್ ದೀದ್ ಇತ್ತ್ಂಡಲಾ. ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಒಂಜಿ ಜಾಗೆಡ್, ಒಂಜಿ ಎಲ್ಯ [[ರಾಕ್ ಗಾರ್ಡನ್|ಆಲ್ಪೈನ್ ಗಾರ್ಡನ್]] (ಅಂದಾಜಿ. {{convert|320|m2|sqft|-2|abbr=on|disp=or}}) ಬೊಕ್ಕ ಒಂಜಿ ಫೌಂಟೇನ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರೆನ [[ಸಫಾರಿ]] ಬೇಟೆದ ಹವ್ಯಾಸೊನು ಒಳಗೊಂಡ ಆರ್ಥಿಕ ದುರ್ಬಳಕೆಡ್ದ್,<ref name="mirtu"/> ಜುಲೈ ೧೯೧೨ಡ್, ಹೊರೊಡೆಕಿ ಕೀವ್ ಮ್ಯೂಚುಯಲ್ ಕ್ರೆಡಿಟ್ ಅಸೋಸಿಯೇಷನ್ ​​ಡ್ದ್ ದೆತೊನುನ ಸಾಲೊಗು ವಿರುದ್ಧವಾದ್ ಕಟ್ಟಡೊನು ಜಾಮೀನು ಆದ್ ವಾಗ್ದಾನ ಮಲ್ತೆರ್.<ref name="riddle">{{cite web|title=Riddle of House with Chimaeras|url=http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|first=Yuliya|last=Ivashko|work=Vash Kiev|access-date=2006-07-14|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20120206003140/http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|archive-date=2012-02-06}}</ref> ಹೊರೊಡೆಕಿ ಸಾಲೊಡು ಡಿಫಾಲ್ಟ್ ಆನಗ, ಕಟ್ಟಡೊನು ೧೯೧೩ಡ್ ಹರಾಜು ಮಲ್ತೆರ್,<ref name="riddle"/> ಬೊಕ್ಕ ಎಂಜಿನಿಯರ್ ಡೇನಿಯಲ್ ಬಾಲಾಖೊವ್ಸ್ಕಿನ ಆಸ್ತಿ ಆಂಡ್,<ref name="mirtu"/> ಕೀವ್ ವ್ಯಾಪಾರಿನ ಮಗೆ,<ref name="KV"/> ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆದ ನಿರ್ದೇಶಕ ಮಂಡಳಿದ ಅಧ್ಯಕ್ಷೆರಾದಿತ್ತೆರ್,<ref name="KV"/> ಬೊಕ್ಕ ಕೀವ್ ಡ್ ಒರಿ [[ಫ್ರಾನ್ಸ್|ಫ್ರೆಂಚ್]] ಕಾನ್ಸುಲಾರ್ ಏಜೆಂಟ್.<ref name="Fyodorovskaya">{{cite web|url=http://www.house-ua.com/news395.html |title=Hunter from the House with Chimaeras |access-date=2006-07-14 |first=Olga |last=Fyodorovskaya |work=Ukrainskiy Dom |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20070929175434/http://www.house-ua.com/news395.html |archive-date=September 29, 2007 }}</ref> ೧೯೧೬ಡ್, ಈ ಇಲ್ಲ್ ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆಗ್ ಸೇರ್ದಿನವು.<ref name="riddle"/> ೧೯೧೮ಡ್, ಕಟ್ಟಡೊದ ಮಾಲೀಕತ್ವ ಮತ್ತೆ ಬದಲಾಂಡ್, ಸ್ಯಾಮ್ಯುಯೆಲ್ ನೆಮೆಟ್ಸ್ ಗ್.<ref name="riddle"/> ೧೯೨೧ಡ್, [[ಬೊಲ್ಶೆವಿಕ್]] ಲು ಕೀವ್ ನ್ ನಿಯಂತ್ರಣ ಮಲ್ತಿನ ಬುಕ್ಕೊ, [[ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್|ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್]] ದ ಕೆಲವು ಇಲಾಖೆಲು ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಸದನೊಡು ಕಚೇರಿಲೆನ್ ದೆತೊನುವೆರ್. ===ಒಡೆತನ ೧೯೨೧–೨೦೦೨=== [[ರಷ್ಯಾದ ಕ್ರಾಂತಿದ ನಂತರದ ಉಕ್ರೇನ್|ಅಶಾಂತಿದ ಅವಧಿ]] [[ರಷ್ಯಾದ ಕ್ರಾಂತಿ (೧೯೧೭)|೧೯೧೭ ರ ರಷ್ಯಾದ ಕ್ರಾಂತಿ]] ನಂತರ, ಕಟ್ಟಡೊನು [[ರಾಷ್ಟ್ರೀಕರಣ|ರಾಷ್ಟ್ರೀಯೀಕರಣ]] ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನನೊಂಜಿ ಸರ್ತಿ [[ಕಮ್ಯುನಲ್ಕಾ|ಸಾಮುದಾಯಿಕ ಜೀವನ]] ಗ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="president.gov.ua">{{cite web|url=http://www.president.gov.ua/en/slideshow/3.html |title=House with Chimeras |access-date=2008-03-20 |work=[[President of Ukraine|Official web-site of President of Ukraine]] |url-status=dead |archive-url=https://web.archive.org/web/20080425002336/http://www.president.gov.ua/en/slideshow/3.html |archive-date=2008-04-25 }}</ref> ಪ್ರತಿಯೊಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಸುಮಾರ್ ಒಂಜಿ ರ್ದ್ ಪತ್ತ್ ಕುಟುಮೊಲು ನೆಲೆಯಾದಿತ್ತೆರ್.<ref name="Horodetskyi"/> [[ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧ|ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧ]] (೧೯೪೧-೧೯೪೩) ದ ಪೊರ್ತುಡು, ಈ ಕಟ್ಟಡೊನು ಬುಡ್ದು ಪೋಯೆರ್. ಯುದ್ಧದ ಸಮಯೊಡು ಕಠಿಣ ಅಂಶೊಲೆಗ್ ಒಡ್ಡಿಕೊಳ್ಳುನ ಕಾರಣ, ಕಟ್ಟಡೊಗು ಅಯಿತ ರಚನೆಗ್ ಮಸ್ತ್ ಹಾನಿ ಆಂಡ್.<ref name="Horodetskyi"/> ಯುದ್ಧೊದ ನಂತರ, ಈ ಕಟ್ಟಡೊನು [[ಇವಾನ್ ಫ್ರಾಂಕೊ ರಾಷ್ಟ್ರೀಯ ಶೈಕ್ಷಣಿಕ ನಾಟಕ ರಂಗಮಂದಿರ|ಇವಾನ್ ಫ್ರಾಂಕೊ ರಂಗಮಂದಿರ]] ಡ್ದ್ ಸ್ಥಳಾಂತರಿಸಾಯಿನ ನಟೆರೆಗ್ ನಿವಾಸವಾದ್ ಸಂಕ್ಷಿಪ್ತವಾದ್ ಬಳಕೆ ಮಲ್ತೆರ್;<ref name="KV"/> ಆಂಡಲಾ, [[ಉಕ್ರೇನಿಯನ್ ಸೋವಿಯೆತ್ ಸಮಾಜವಾದಿ ಗಣರಾಜ್ಯ]] ದ [[ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಫ್ ಉಕ್ರೇನ್|ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ]] ದ ಕೇಂದ್ರ ಸಮಿತಿ ಈ ಕಟ್ಟಡೊದ ಒಡೆತನೊನು ದೆತೊನುಂಡು ಬೊಕ್ಕ ಅಯಿನ್ ಅಕ್ಲೆನ ಗಣ್ಯೆರೆಗ್ [[ಪಾಲಿಕ್ಲಿನಿಕ್]] ನಂಬರ್ ೧ ಆದ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="Horodetskyi"/> ಪಾಲಿಕ್ಲಿನಿಕ್ 20ನೆ ಶತಮಾನೊದ ಅಂತ್ಯೊಗು ಮುಟ್ಟ ಈ ಕಟ್ಟಡೊನು ಬಳಕೆ ಮಲ್ತ್ಂಡ್. ಆ ಸಮಯೊಡು, ಕಟ್ಟಡ ಬಹುತೇಕ ಅರ್ಧೊಗು ಪುಡಾಂಡ್. ಒಂಜಿ ಭಾಗ {{convert|22|cm|in|0|abbr=on}} ನ್ ಸಾಗ್ ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ ಒಂಜಿ ಪ್ರಮುಖ ಲಂಬ ಬಿರುಕು ರೂಪುಗೊಂಡುಂಡು, ಉಂದೆತ ಅಗಲ ಸುಮಾರ್ {{convert|40|cm|in|0|abbr=on}} ಉಂಡು.<ref name="Horodetskyi"/> ಕಟ್ಟಡೊದ ಕೆಲವು ವಾಸ್ತುಶಿಲ್ಪದ ವಿವರೊಲು ಒಂಜಿ ವೇಳೆ ಚಿಪ್ ಆತ್ಂಡ್, ಅತ್ತ್ಂಡ ಬಿರುಕು ಬುಲೆದ್ಂಡ್. ಕಟ್ಟಡೊದ ಜೀರ್ಣೋದ್ಧಾರದ ಬೇಲೆನ್ 2002 ಗ್ ನಿಗದಿ ಮಲ್ತೆರ್, ಆಂಡಲಾ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಆಪರೇಟರ್ ಲು 40 ವರ್ಸೊಡ್ದ್ ಜಾಸ್ತಿ ಕಟ್ಟಡೊನು ಆಕ್ರಮಣ ಮಲ್ತ್ ದ್ ಬುಡುದು ಪೋಯೆರೆ ಹಿಂಜರಿಯೊಂದಿತ್ತೆರ್. ಕಟ್ಟಡೊಡ್ದ್ ನಿವಾಸಿಲೆನ್ ಪಿದಯಿ ಕನರೆ, ಕೆಲಸದಕುಲು ಮಾತಾ ಕಿಟಕಿಲೆನ್ ಮುಚ್ಚಿಯೆರ್ ಬೊಕ್ಕ ಪಾಲಿಕ್ಲಿನಿಕ್ ಆವರಣೊನು ಖಾಲಿ ಮಲ್ತ್ಜಿಂಡ ಬಾಗಿಲುಲೆನ್ ಲಾ ಅಂಚನೆ ಮಲ್ಪುವೆ ಪಂದ್ ಬೆದರಿಕೆ ಮಲ್ತೆರ್. ಈ ವಿಷಯೊಡು [[ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ|ಅಧ್ಯಕ್ಷೆರ್]] ನ ಪಾಲ್ ಪಡೆಯಿನೆಡ್ದಾವರ ಮಾತ್ರ ಪಾಲಿಕ್ಲಿನಿಕ್ ನ್ ಸಂಪೂರ್ಣವಾದ್ ಪಿದಯಿ ಬುಡ್ಪಾಯೆರ್.<ref name="mirtu"/> ===ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಅಧಿಕೃತ ಬಳಕೆ=== [[File:Flags of EU and Ukraine.jpg|thumb|ಒಂಜಿ ಅಧಿಕೃತ ಕಾರ್ಯಕ್ರಮೊದ ಪೊರ್ತುಡು ಕಟ್ಟಡೊದ ಎದುರು ಉಕ್ರೇನ್ ಬೊಕ್ಕ [[ಯುರೋಪಿಯನ್ ಒಕ್ಕೂಟ]] ದ ಧ್ವಜೊಲು]] ಜೀರ್ಣೋದ್ಧಾರದ ಸಮಯೊಡು, ''ಪ್ರಾಜೆಕ್ಟ್ ರೆಸ್ಟಾವ್ರಾಟ್ಸಿಯಾ'' ನಡಪಾಯಿನ ಬೊಕ್ಕ ನಟಾಲಿಯಾ ಕೊಸೆಂಕೊ ಮುಖ್ಯಸ್ಥೆರಾದ್,<ref name="vr-829-р">{{Cite Ukrainian law |type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=829-р|law=829-2003-р |name=On the recognition of the project and the company carrying out the building's reconstruction as well as the restoration of the monument of architecture and the government monument of the city on Bankova St., 10 in the city of Kyiv|date=2003-12-31}}</ref> ಕಟ್ಟಡದ ಅಡಿಪಾಯೊನು ಬಲಪಡಾಯೆರೆ [[ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟ|ಸೋವಿಯೆತ್]] ಕಾಲೊಡು ತುಂಬಿನ ಇಡೀ ಕೆಳಮಹಡಿನ್ ಕೆಲಸದಕುಲು ಕಂಡು ಕನತೆರ್.<ref name="mirtu"/> ಒಳಾಂಗಣದ ವಿಸ್ತಾರವಾದ್ ಅಲಂಕಾರೊನು ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪೊಡಾಂಡ್. ಅಂಗಳೊಡು, ಪುನಃಸ್ಥಾಪಕೆರ್ ಒಂಜಿ ಕೃತಕ ಕೆರೆ, ಕಾರಂಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಎಲ್ಯ ಉದ್ಯಾನೊನು ದೀಯೆರ್—ಉಂದು ಮಾತ ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆಡ್ ಇತ್ತ್ಂಡ್.<ref name="mirtu"/> ಈ ಕಟ್ಟಡೊನು ನವೆಂಬರ್ ೨೦೦೪ಡ್ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ "ಉಕ್ರೇನಿಯನ್ ಕಲೆತ ಮೇರುಕೃತಿಲು" ಆದ್ ತೆರೆಯೆರ್.<ref name="museum">{{cite news|url=http://www.korespondent.net/main/31543 |title=A cultural museum center opens in the House with Chimaeras |access-date=2007-07-12 |date=2004-11-19 |work=[[Korrespondent]] |publisher=Bigmir-Internet |language=uk |archive-url=https://web.archive.org/web/20070927234156/http://www.korespondent.net/main/31543 |archive-date=September 27, 2007 |url-status=dead }}</ref> ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಆದ್ ಬೊಕ್ಕ ರಾಜ್ಯೊದ ಸಂದರ್ಶಕೆರೆಗ್ ಅಧ್ಯಕ್ಷೀಯ ಸಭೆತ ಜಾಗೆ ಆದ್ ದ್ವಿಗುಣ ಉದ್ದೇಶೊಗು ಸೇವೆ ಮಲ್ಪುಂಡು ಪನ್ಪಿನ ನಿರೀಕ್ಷೆ ಇತ್ತ್ಂಡ್. ಏಪ್ರಿಲ್ ೨೦೦೫ಡ್, [[ಕೀವ್ ಸಿಟಿ ಕೌನ್ಸಿಲ್]] ಒಂಜಿ ಮಸೂದೆನ್ ಸಲ್ಲಿಸಯೆರ್<ref>[http://www.kmr.gov.ua/decree.asp Decrees of Kyiv City Council] {{webarchive|url=https://web.archive.org/web/20070707053720/http://kmr.gov.ua/decree.asp |date=2007-07-07 }} [[Kyiv City Council|Kyiv City Council decree]] No. '''380/2955''': [http://www.kmr.gov.ua/decree.asp?Id=2235 On the allowance of the change of ownership to the government authority with permission of conducting reconstruction and restoration works on the building of 10, Bankova Street] {{webarchive|url=https://web.archive.org/web/20070927225321/http://www.kmr.gov.ua/decree.asp?Id=2235 |date=2007-09-27 }}. Passed on 2005-04-21. {{in lang|uk}}</ref> [[ಉಕ್ರೇನಿಯನ್ ಹೈವ್ನಿಯಾ|₴]]104 ಮಿಲಿಯನ್ (ಅಂದಾಜಿ [[ಯುನೈಟೆಡ್ ಸ್ಟೇಟ್ಸ್ ಡಾಲರ್|ಯುಎಸ್$]]20 ಮಿಲಿಯನ್) ಗ್ [[ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರ|ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರ]] ಗ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮನೆತ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆಗ್.<ref name="proUA">{{cite news|url=http://ua.proua.com/news/2005/05/11/180559.html|archive-url=https://web.archive.org/web/20070930044045/http://ua.proua.com/news/2005/05/11/180559.html|url-status=dead|archive-date=2007-09-30|title="House with Chimaeras" received the status of an official governmental residence|access-date=2006-09-30|date=2004-11-19|work=proUA|language=uk}}</ref> ಅಧಿಕೃತ ಸಮಾರಂಭೊಲೆಡ್ ಬಳಕೆ ಮಲ್ಪುನ ಕಟ್ಟಡೊದ ಎದುರುಡು ಒಂಜಿ ಪೊಸ ಚೌಕೊನು (ಎಲ್ಲಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ನ್ ಮುಚ್ಚುನ) ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆಲಾ ಕೌನ್ಸಿಲ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರೊಗು ಅನುಮತಿ ಕೊರ್ತುಂಡು.<ref name="proUA"/> ಮೇ ೨೦೦೫ಡ್ದ್, ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಅಧಿಕೃತ [[ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆರ್|ಅಧ್ಯಕ್ಷೆರೆನ]] ನಿವಾಸ ಆದುಂಡು, ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಉಪಯೋಗ ಆಪುಂಡು.<ref name="vr-340"/><ref>{{Cite web |title=Ukrainian architecture: 10 buildings that everyone should see |url=https://rubryka.com/en/article/architecture-of-ukraine/}}</ref><ref name="BBC Ukrainian"/> ದಿ ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್ ನ್ ಉಕ್ರೇನಿಯನ್ ಅಧ್ಯಕ್ಷೆರ್ [[ವಿಕ್ಟರ್ ಯುಶ್ಚೆಂಕೊ]] ಬೊಕ್ಕ [[ರಷ್ಯಾದ ಅಧ್ಯಕ್ಷೆರ್|ರಷ್ಯಾದ ಅಧ್ಯಕ್ಷೆರ್]] [[ವ್ಲಾಡಿಮಿರ್ ಪುಟಿನ್]] ನಡುಟು ಒಂಜಿ ಸಭೆತ ಜಾಗೆ ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್, ಅಪಗ ಚಿಮೇರಾಸ್ ಡಿಸೆಂಬರ್ ೨೨, ೨೦೦೬ಡ್ ಕೀವ್ ಗ್ ಭೇಟಿ ಕೊರ್ತೆರ್.<ref>{{cite news|url=http://www.korrespondent.net/main/174172 |title=Yushchenko and Putin decided their goals for two years ahead|access-date=2007-07-12|date=2006-12-22|work=[[Korrespondent]]|publisher=Bigmir-Internet|language=ru}}</ref> ಕಟ್ಟಡೊಡು ಮಾತುಕತೆ, [[with:tête-à-tête|tête-à-tête]] ಪಾತೆರ, ಅಧಿಕೃತ ದಾಖಲೆಲೆಗ್ ಸಹಿ ಪಾಡುನ ಕೊಠಡಿಲು, ಅಂಚನೆ ಪತ್ರಿಕೆಲೆಗ್ ವಿಶೇಷ ಕೊಠಡಿ ಸೇರ್ದ್ ಉಂಡು.<ref name="president.gov.ua"/> ==ವಾಸ್ತುಶಿಲ್ಪ== [[File:Architectural details on House with Chimaeras 2007.JPG|thumb|250px|ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ವಿನ್ಯಾಸಗೊಳಿಸಾಯಿನ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಉಭಯಚರೊಲು ಬೊಕ್ಕ ಬೇತೆ ಜೀವಿಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರತಿಮೆಲೆನ ವಿವರವಾದ್ ನೋಟ]] {| cellpadding="1" style="float:left; border:1px solid #8888aa; padding:5px; font-size:95%; margin:0 15px 0 0; background:#f9f9f9; width:200px;" |- | architectural_style=" text-align: center;" |ವೊಲೊಡಿಮಿರ್ ಯಾಸಿಯೆವಿಚ್ <ref name="wood">{{harvnb|Yasiievych|1988|p=93.}}</ref> |- |<small>ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಇಲ್ಲ್ ನ್ [[ಆಂಟೋನಿ ಗೌಡಿ]] ನ ಕೆಲಸೊಲೆನೊಟ್ಟುಗು ಮಾತ್ರ ಹೋಲಿಕೆ ಮಲ್ಪೊಲಿ, ವಿಶೇಷವಾದ್ [[ಬಾರ್ಸಿಲೋನಾ]], [[ಸ್ಪೇನ್]] ಡ್ ಪ್ರಸಿದ್ಧ [[ಕಾಸಾ ಮಿಲಾ]], ಒಂಜಿ ರಡ್ಡ್ ವರ್ಷೊಡ್ದು ಬೊಕ್ಕ (೧೯೦೫-೧೯೧೦) ನಿರ್ಮಾಣ ಆಯಿನ ನಡುಟು.</small> |} ಈ ಕಟ್ಟಡೊನು [[ಆರ್ಟ್ ನೌವಿಯೊ]] ಶೈಲಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಉಂದು ಆ ಕಾಲೊಡು ತುಲನಾತ್ಮಕವಾದ್ ಪೊಸ ಶೈಲಿ ಆದಿತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಹರಿಯುನ, ವಕ್ರರೇಖೆದ ವಿನ್ಯಾಸೊಲೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್, ಉಂದೆಟ್ ಮಸ್ತ್ ಸರ್ತಿ ಪುಷ್ಪ ಬೊಕ್ಕ ಬೇತೆ ಸಸ್ಯ-ಪ್ರೇರಿತ ಮೋಟಿಫ್ ಲೆನ್ ಸೇರಾವೊಂದಿತ್ತೆರ್. ಹೊರೊಡೆಕಿ ಕಟ್ಟಡದ ಪಿದಯಿದ ಅಲಂಕಾರೊಡು ಪೌರಾಣಿಕ ಜೀವಿಲೆನ ಬೊಕ್ಕ ಮಲ್ಲ ಆಟದ ಪ್ರಾಣಿಲೆನ ರೂಪೊಡು ಇಂಚಿನ ಮೋಟಿಫ್ ಲೆನ್ ತೋಜಾದ್ ಕೊರ್ತೆರ್. ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮಲ್ತಿನ ಇಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಬೇಲೆಡ್ದ್ ಕೀವ್ ದ [[ಆಂಟೋನಿ ಗೌಡಿ|ಗೌಡಿ]] ಪನ್ಪಿನ ಅಡ್ಡ ಪುದರ್ ನ್ ಗಳಸೊಂಡೆರ್.<ref name="mirtu"/><ref name="element"/> ಕಟ್ಟಡ ಉಪ್ಪುನ ಕಡಿಮೆ ಇಳಿಜಾರುಡ್ದ್, ಅಯಿತ ಅಡಿಪಾಯೊಗು ಸರಿಯಾದ್ ಸರಿ ಆಯೆರೆ ಕಾಂಕ್ರೀಟ್ ಡ್ದ್ ವಿಶೇಷವಾದ್ ವಿನ್ಯಾಸಗೊಳಿಸೊಡಾಪುಂಡು. ಎದುರುಡ್ದ್ ತೂನಗ, ಕಟ್ಟಡೊಗು ಮೂಜಿ ಮಹಡಿಲು ಮಾತ್ರ ಉಪ್ಪುನಂಚ ತೋಜುಂಡು. ಆಂಡಲಾ, ಪಿರವುಡ್ದ್, ಅಯಿತ ಆಜಿ ಮಹಡಿಲೆನ್ ತೂವೊಲಿ.<ref name="vlasenko">{{harvnb|Zharikov|1983–1986|p=38.}}</ref>ಕಟ್ಟಡದ ಅಡಿಪಾಯೊದ ಒಂಜಿ ಭಾಗೊನು ಕಾಂಕ್ರೀಟ್ ರಾಶಿಲೆಡ್ ಮಲ್ತೆರ್, ಬೊಕ್ಕೊಂಜಿ ಭಾಗೊನು ನಿರಂತರ ಅಡಿಪಾಯೊದ ರೂಪೊಡು ಮಲ್ತೆರ್. ಸಾಮಾನ್ಯವಾದ್, ಈ ರಡ್ಡ್ ವಿಧಾನೊಲು ಎಡ್ಡೆ ರೀತಿಡ್ ಮಿಶ್ರಣ ಆಪುಜಿ ಆಂಡ ಹೊರೊಡೆಕಿ ಒಂಜಿ ರೀತಿಡ್ ಈ ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆನ್ ಜಯಿಪುನಗ ಯಶಸ್ವಿಯಾಯೆರ್. ಇಟಾಲಿಯನ್ ಶಿಲ್ಪಿ [[ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ)|ಎಮಿಲಿಯೊ ಸಾಲಾ]] ಆಂತರಿಕ ಬೊಕ್ಕ ಬಾಹ್ಯ ಶಿಲ್ಪಕಲೆತ ಅಲಂಕಾರೊಲೆಗ್ ಜವಾಬ್ದಾರರಾದಿತ್ತೆರ್, ಉದಾಹರಣೆಗೆ [[ಮತ್ಸ್ಯಕನ್ಯೆ]] ಲು, ಡಾಲ್ಫಿನ್ ಲು, ಬೊಕ್ಕ ಕಟ್ಟಡದ ಛಾವಣಿಡ್ ಕಪ್ಪೆಲು, ಪಿದಯಿದ ಗೋಡೆಲೆಡ್ ಮುಳುಗಡೆ ಆಪಿನ ನೌಕೆಲು ಬೊಕ್ಕ ಬೇಟೆದ ಟ್ರೋಫಿಲು, ಬೊಕ್ಕ ಒಳಾಂಗಣ ಅಲಂಕಾರೊಲು, ಉದಾಹರಣೆಗೆ ಮೆಟ್ಟಿಲುಲು ಬೊಕ್ಕ ಮಲ್ಲ ವೇಲು [[ಚಾಂಡೆಲಿಯರ್]]ಲು [[ನಿಂಫೆಯಾ|ಕಮಲ]] ಪುಷ್ಪೊಲೆನ ಕಾಂಡೊಲೆಡ್ ಕಚ್ಚಿದ್ ದೀತಿನ ಮಲ್ಲ [[ಕ್ಯಾಟ್ ಫಿಶ್]] ನ್ ಚಿತ್ರಿಸವುಂಡು. ಸಾಲಾ ಮಲ್ತಿನ ಪಿದಯಿದ ಶಿಲ್ಪೊಲು [[ಸಿಮೆಂಟ್]] ಡ್ದ್ ಮಲ್ಪುನವು. ಸಿಮೆಂಟ್ ದ ಉತ್ಪಾದನೆ «ಫಾರ್» ಕಂಪೆನಿಡ್ ಇತ್ತ್ಂಡ್, ಅಯಿಟ್ ಹೊರೊಡೆಕಿ ಸಹ-ನಿರ್ದೇಶಕೆರ್ ಆದಿತ್ತೆರ್.<ref name="klymenko">{{cite web|url=http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|archive-url=https://web.archive.org/web/20050310062635/http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|url-status=dead|archive-date=2005-03-10|title=Budynok z Khimeramy|access-date=2006-09-30|first=Sergiy|last=Klymenko|work=Photos of Kyiv|language=uk, ru}}</ref> ಕಂಪೆನಿದ ಮುಖ್ಯ ನಿರ್ದೇಶಕೆರ್ ರಿಚ್ಟರ್ ನ ಕೋರಿಕೆದ ಪ್ರಕಾರ ಸಿಮೆಂಟ್ ನ್ ಪ್ರಾಥಮಿಕ ಕಟ್ಟಡ ಸಾಮಗ್ರಿ ಆದ್ ಮಾತ್ರ ಬಳಕೆ ಮಲ್ತೆರ್.<ref>{{cite web|url=http://www.narodnapravda.com.ua/travel/468d12ca51334/|title=Walks around Kyiv. House with Chimaeras|access-date=2006-07-14|author=explorer|date=2007-07-05|work=Narodna pravda|language=uk|archive-date=2011-07-25|archive-url=https://web.archive.org/web/20110725230507/http://www.narodnapravda.com.ua/travel/468d12ca51334/|url-status=dead}}</ref>ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಸಿಮೆಂಟ್ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿ ಆದ್ ಜನಪ್ರಿಯ ಆದಿಜ್ಜಿ, ಅಂಚಾದ್ ಅಯಿತ ಬಳಕೆನ್ ಇಲ್ಲ್ ಬೊಕ್ಕ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿಲೆಗ್ ಪ್ರಚಾರೊದ ರೂಪೊಡು ಬಳಕೆ ಮಲ್ತೆರ್.<ref name="klymenko"/> <gallery class="center" widths=250 heights=185> File:Architectural details on House with Chimaeras 2007-2.JPG|ಜಿಂಕೆ ಬೊಕ್ಕ [[ಖಡ್ಗಮೃಗ]]ಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ವಿಸ್ತಾರವಾದ್ ಸಿಮೆಂಟ್ ಅಲಂಕಾರೊಲು File:Elephant on House with Chimaeras.JPG|ಈ ಕಟ್ಟಡೊಡು ಬೇತೆ ಬೇತೆ ಕೆತ್ತನೆಲು ಉಂಡು, ಉದಾಹರಣೆಗೆ ಈ ಆನೆ-ತಲೆ [[ಗಾರ್ಗೊಯ್ಲ್]] File:Architectural details on House with Chimaeras 2007-3.JPG|ಕಟ್ಟಡೊದ ಸುತ್ತ ತೂಯಿನ ಮಸ್ತ್ ಮೂರ್ತಿಲೆಡ್ ಒಂಜಿ </gallery> ===ನೆಲತ ಯೋಜನೆ=== [[File:House with Chimaeras (Plan).JPG|thumb|ಆಜನೇ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅರೆನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಹೊರೊಡೆಕಿನ ಮೂಲ ನೀಲನಕ್ಷೆಲು; ೧೯೦೦ ದಶಕದ ಸುರುಟು]] ಚಿಮೇರಾಸ್ ನ ಇಲ್ಲ್ ನ್ ಬಾಡಿಗೆದಾರೆರ್ ಇಡೀ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ಪುನಂಚಿನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಡ್ ಖಾಸಗಿ ಅಡುಗೆಮನೆಡ್ದ್ ಎಲ್ಯ ಪುಡಿ ಕೊಠಡಿಲೆಗ್ ಮುಟ್ಟ ಅಗತ್ಯವಾಯಿನ ಮಾತಾ ಇಲ್ಲದ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಕಟ್ಟಡೊದ ಉಲಯಿ ಉಪ್ಪುನ ತೆರೆದ ನೆಲತ ಯೋಜನೆ ಬೊಕ್ಕ ಹೆಚ್ಚುವರಿ ಕೊಠಡಿಲು 20ನೆ ಶತಮಾನೊದ ಸುರುತ ಶ್ರೀಮಂತೆರೆನ ಇಲ್ಲ್ ಲೆನ ಲಕ್ಷಣೊಲು.<ref name="vlasenko"/> ಒಟ್ಟುಗು, ಕಟ್ಟಡೊದ ವಿಸ್ತೀರ್ಣ {{convert|3309.5|m2|sqft|2|abbr=on}} ಉಂಡು.<ref name="vr-829-р"/> ಗುಡ್ಡೆದ ಆಳೊಡು ಇಪ್ಪುನ ಕಟ್ಟಡೊದ ಕೆಳಮಟ್ಟೊಡು ರಡ್ಡ್ [[ಸ್ಟೆಬಲ್]] ಲು, [[ಕೋಚ್ ಮ್ಯಾನ್|ಕೋಚ್ ಮೆನ್]] ಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಪಾಲ್ ಪಡೆಯಿನ ಬಟ್ಟೆ ಒಯ್ಪುನ ಜಾಗೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಪ್ರತ್ಯೇಕ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್. ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಸ್ಟೋರೇಜ್ ರೂಮ್ ಇತ್ತ್ಂಡ್. ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಸುರುತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ರಡ್ಡ್ ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್, ಬೊಕ್ಕ ರಡ್ಡನೆಡ್ ಮೂಜಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್.{{refn|೧೯೦೩ಡ್ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ ೫೪೦ ಬೊಕ್ಕ ೪೨೦ ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್, ಅಯಿಕ್ ಅನುಗುಣವಾದ್.{{sfn|Malakov|1999}}|group=nb}} ಕೆಳಮಟ್ಟೊದ ಮಿತ್ತ್ ದ ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿನ್ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಮಾತ್ರ ದೀಯೆರೆ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ರಡ್ಡನೆ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಫೋಯರ್, ಅಡುಗೆಮನೆ, [[ಬಫೆಟ್]], ಮೂಜಿ ಸೇವಕೆರೆನ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ಆರು ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಒಂಜೇ ಮಟ್ಟೊಡು ನಾಲ್ [[ವೈನ್ ಸೆಲ್ಲರ್]] ಲಾ ಇತ್ತ್ಂಡ್.{{refn|Tಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಬೆಲೆ 1903ಡ್ 1,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ನೆಲಮಾಳಿಗೆಲು ಮಿತ್ತ್ ದ ಮಟ್ಟೊಡು ಉಪ್ಪುನ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲೆಗ್ ಸೇರ್ದಿನವು. ಮೂಜಿನೆ ಮಹಡಿಡ್, ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಎಂಟು ವಸತಿ ಕೊಠಡಿಲು, ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, [[ಸ್ಕಲ್ಲರಿ (ಕೋಣೆ)|ಪಾತ್ರೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ]], ಸೇವಕೆರೆಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು ಇತ್ತ್ಂಡ್.{{refn|ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸುರುತ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ 2,000 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ [[ಬಂಕೋವಾ ಸ್ಟ್ರೀಟ್]] ದ ಮಟ್ಟೊಡ್ದ್ ಸ್ವಲ್ಪ ಕೆಳಗ್ ದೀಯೆರ್, ಎದುರುದ ಪ್ರವೇಶ ದ್ವಾರೊಡ್ದ್. ಹೊರೊಡೆಕಿಗ್ ಸೇರ್ದಿನ ಮಲ್ಲ ಅಪಾರ್ಟ್ಮೆಂಟ್, ಒಂಜಿ [[ಅಧ್ಯಯನ (ಕೋಣೆ)|ಅಧ್ಯಯನ]], ಒಂಜಿ [[ಮಲ್ಲ ಕೋಣೆ]] ಬೊಕ್ಕ ಒಂಜಿ [[ಲಿವಿಂಗ್ ರೂಮ್]], ಒಂಜಿ [[ಡೈನಿಂಗ್ ರೂಮ್]], ಒಂಜಿ ಬೌಡೋಯಿರ್, ಒಂಜಿ [[ಬೆಡ್ ರೂಮ್]], ಒಂಜಿ ಜೋಕುಲೆನ ಕೋಣೆ, ಒಂಜಿ [[ಗವರ್ನೆಸ್]] ಗ್ ಒಂಜಿ ಕೋಣೆ, ಒಂಜಿ ಅತಿಥಿ ಕೋಣೆ, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೋಣೆ, ಅಡುಗೆಮನೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಒಂಜಿ ಕೋಣೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲು.<ref name="Horodetskyi"/> ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಹೊರೊಡೆಕಿನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸಮಾನವಾದ್ ಗಾತ್ರ ಬೊಕ್ಕ ವಿನ್ಯಾಸೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಇತ್ತ್ಂಡ್.{{refn|ಈ ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆನ್ 1903ಡ್ ವಾರ್ಷಿಕ 3,500 ರೂಬಲ್ಸ್ ಗ್ ಬಾಡಿಗೆಗ್ ಕೊರ್ತೆರ್.{{sfn|Malakov|1999}}|group=nb}} ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಕೋಣೆ ಕಮ್ಮಿ ಇತ್ತ್ಂಡ್; ಇಂದೆನ್ ಸರಿ ಮಲ್ಪರೆ, ನಗರೊದ ವಿಹಂಗಮ ದೃಶ್ಯೊನು ಒದಗಾವುನ ಒಂಜಿ ಸಂಪರ್ಕ ಟೆರೇಸ್ ಇತ್ತ್ಂಡ್.<ref name="Horodetskyi"/>{{refn|The rental cost for this dwelling was 2,750 rubles annually. For comparison, an average salary for a librarian was about 50 rubles per year.{{sfn|Ivanenko|2004}}|group=nb}} ==Legends== [[File:House with Chimaeras gardens.JPG|thumb|ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಉದ್ಯಾನೊಲು [[ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡ (ಕೀವ್)|ಅಧ್ಯಕ್ಷೀಯ ಕಟ್ಟಡ]] ಹಿನ್ನೆಲೆಡ್ ತೋಜುಂಡು]] ವರ್ಷೊಡ್ದಿಂಚಿ, ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಪ್ಪುನ ಇಲ್ಲ್ ದ ಅಸಾಮಾನ್ಯ ಸ್ವಭಾವೊ ಮಾರ್ಗದರ್ಶಿ-ಪುಸ್ತಕೊಲೆಡ್ ಅತ್ತಂದೆ ಪತ್ರಿಕೆಲೆಡ್ ಕೆಲವೊಂಜಿ ಸರ್ತಿ ಪುನರಾವರ್ತಿತವಾದ್ ಬರ್ಪಿನ ಒಂಜಿ ಸಂಖ್ಯೆದ ಕತೆಕುಲೆಗ್ ಕಾರಣವಾತ್ಂಡ್, ಆಂಡಲಾ ಅವು ಅಸತ್ಯ ಅತ್ತಂದೆ ಯಾವುದೇ ಪರಿಶೀಲಿಸಬಹುದಾದ ಮೂಲೊನು ಕಳೆವೊಂದಿಜ್ಜಿ.<ref>{{harvnb|Savchuk|1996|p=95.}}</ref> ಸುರುತ ದಂತಕಥೆದ ಪ್ರಕಾರ, ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಮಗೆ ಕೆಲವು ದುರದೃಷ್ಟಕರ ಪ್ರೇಮ ಸಂಬಂಧೊಡ್ದ್ ಅತ್ತಂಡ ಕುಟುಂಬದ ಜಗಳೊಡ್ದ್ [[ಡ್ನಿಪರ್ ನದಿ]] ಗ್ ಜಂಪ್ ಮಲ್ತ್ ದ್ ಆತ್ಮಹತ್ಯೆ ಮಲ್ತೆರ್.{{refn|It was actually previous owner's ([[Friedrich Mering|Professor Mering]]) daughter who drowned.{{sfn|Ivanenko|2004}}|group=nb}}ಫಲವಾದ್, ಹೊರೊಡೆಕಿ ಸ್ವಲ್ಪ ಪಿಚ್ಚರ್ ಆದ್ ತನ್ನ ಮಗೆನ ನೆಂಪುಗು ಈ ಕತ್ತಲೆದ ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="kievinfo">{{cite web|url=http://www.kiev.info/culture/house_chimeras.htm|title=House with Chimaeras|access-date=2006-02-16|first=Viktor|last=Pavlovsky |work=Kiev Info|publisher=Optima Tours}}</ref> ರಡ್ಡನೆ ದಂತಕಥೆ ಪಂಡ ಹೊರೊಡೆಕಿ ಕೆಲವು ಬೇತೆ ವಾಸ್ತುಶಿಲ್ಪಿಲೆನೊಟ್ಟುಗು ಒಂಜಿ ಪಂತೊನು ಮಲ್ತೆರ್, ವಾಸ್ತುಶಿಲ್ಪಿ ಅಲೆಕ್ಸಾಂಡರ್ ಸ್ಕೋಬೆಲೆವ್ ಸೇರ್ದ್, ಅಂಚನೆ ಭೂಪ್ರದೇಶೊಡು ಒಂಜಿ ಇಲ್ಲ್ ಕಟ್ಟೆರೆ ಅಸಾಧ್ಯ ಪನ್ಪಿನೆನ್ ಸಾಬೀತು ಮಲ್ಪೆರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್, ದಾಯೆ ಪಂಡ ಆ ಜಾಗೆ ([[ಇವಾನ್ ಫ್ರಾಂಕೊ]] ಥಿಯೇಟರ್ ದ ಸಮೀಪೊಡು) ಒಂಜಿ ಜವುಗು (''ಕೊಜ್'ಯೆ ಬೊಲೊಟೊ'') ಮಿತ್ತ್ ಉಂಡು. ಕೀವ್ ದ ನಿರ್ಮಾಣ ಸಮಿತಿ ಈ ನಿರ್ದಿಷ್ಟ ಲಾಟ್ ಡ್ ಏರ್ಲಾ ರಚನೆಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ನಿಷೇಧ ಮಲ್ತ್ಂಡ್, ಆಂಡ ಅಂಚನೆ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಡು ಹೊರೊಡೆಕಿ ಪಂತೊನು ಗೆಂದಿಯೆರೆ ಅನುವು ಮಲ್ತೆರ್.<ref name="kievinfo"/> ಮೂಜಿನೆ ದಂತಕಥೆದ ಪ್ರಕಾರ, ಹೊರೊಡೆಕಿ 1913ಡ್ ಶಾಪ ಕೊರ್ತೆರ್ (ಅರೆನ [[ಫೋರ್ಕ್ಲೋಸರ್|ಅರೆನ ಸಾಲಗಾರೆರೆಗ್ ಮರುಪಾವತಿ ಮಲ್ಪೆರೆ ಅಸಮರ್ಥತೆ]] ಕಾರಣ); ಇಲ್ಲ್ ದ ಮಾತಾ ಬಾಡಿಗೆದಾರೆರ್ ದುಖಿಡ್ ಉಪ್ಪುವೆರ್ ಅತ್ತ್ಂಡ ಒಂಜಿ ರೀತಿದ ಆರ್ಥಿಕ ದುರದೃಷ್ಟೊನು ಎದುರಿಸವೆರ್. ಕಟ್ಟಡೊದ ಒಂಜಿ ಭಾಗೊನು ಬಾಡಿಗೆಗ್ ದೆತೊನುನ ಮಾತಾ ವ್ಯವಹಾರೊಲು ಒಂಜಿ ವೇಳೆ [[ದಿವಾಳಿ]] ಪೋಯೆರ್, ಅಕ್ಲೆನ ನಿಧಿನ್ ಕಳವು ಮಲ್ತೆರ್ ಅತ್ತ್ಂಡ ರದ್ದು ಮಲ್ತೆರ್ ಪನ್ಪಿನ ಒಂಜಿ ಕತೆ ಉಂಡು.<ref name="mirtu"/> == ಉಲ್ಲೇಖೊಲು == === ಟಿಪ್ಪಣಿಲು === {{Reflist|group="nb"|30em}} === ಅಡಿಟಿಪ್ಪಣಿಲು === {{Reflist|30em}} === ಗ್ರಂಥಸೂಚಿ === {{Refbegin}} * {{Cite journal|last=Ivanenko|first=Anna|title=Horodecki. Lord of Chimaeras|url=http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|volume=1–2|journal=Mir Turizma|year=2004|location=[[Kyiv]]|issn=1811-3583|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20070506082208/http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|archive-date=2007-05-06}} * {{Cite book|last=Malakov|first=Dmytro|title=Arkhitektor Horodetskyi|publisher=Kyi|year=1999|location=[[Kyiv]]|isbn=966-7161-30-7|language=uk}} * {{Cite book|last=Malikenaite|first=Ruta|title=Touring Kyiv|publisher=Baltija Dryk|year=2003|isbn=966-96041-3-3|language=uk}} * {{Cite book|last=Savchuk|first=Galina|title=The Streets of Kiev: Five Walks in the Centre|publisher=ArtEc Publishers|year=1996|location=[[Kyiv]]}} * {{Cite book|last=Yasiievych|first=V. E.|title=Architecture of Ukraine at the Turn of the 19th–20th Centuries|publisher=Budivel'nyk|year=1988|location=[[Kyiv]]|isbn=5-7705-0088-3|oclc=19693625|language=ru}} * {{Cite book|last=Zharikov|first=N. L.|title=Monuments of Urban Development and Architecture in the Ukrainian SSR|chapter=Residential house (Horodecki's), 1904|chapter-url=http://ua.vlasenko.net/_pgs/pgs-html/pgs1-038.html|volume=1–4|publisher=Budivel'nyk|year=1983–1986|location=[[Kyiv]]|language=ru|lccn=84179019}} {{Refend}} ==ಬಾಹ್ಯ ಕೊಂಡಿಲು== {{Commons category|House with Chimeras (Kyiv)}} * {{Cite web|url=http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|title=Art Nouveau in Kyiv|first=Dmytro|last=Horbachov|access-date=2006-09-30|archive-url=https://web.archive.org/web/20041104061652/http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|archive-date=2004-11-04|url-status=dead|ref=none}} * {{Cite encyclopedia|url=http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|title=House with Chimaeras|encyclopedia=Wiki-Encyclopedia Kyiv|language=uk|access-date=2011-06-21|archive-date=2022-03-07|archive-url=https://web.archive.org/web/20220307105847/http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|url-status=dead}} * {{Cite web|url=http://www.interesniy.kiev.ua/old/architecture/buildings/2 |script-title=ru:Замки Киева: Легенды и были Замка Ричарда, Дом с Химерами, замок барона Штейнгеля|trans-title=Castles of Kiev: Legends were Richard's Castle, the House of Chimeras, Baron Steingel's castle |work=Interesniy Kiyev |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20061104033503/http://www.interesniy.kiev.ua/old/architecture/buildings/2 |archive-date=2006-11-04 }} * {{Cite web|url=http://brmr.livejournal.com/628752.html |title=Photogallery containing detailed images |publisher=[[LiveJournal]] |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20130814171250/http://brmr.livejournal.com/628752.html |archive-date=2013-08-14 }} * {{Cite web|url=http://www.domparketa.com.ua/ru/gallery/dom_ximer/ |title=Interior photogallery |work=domparketa.com.ua |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20060829142840/http://domparketa.com.ua/ru/gallery/dom_ximer/ |archive-date=2006-08-29 }} tqqgt703unjtmfa06mb6ct5eqxepp5k 366203 366175 2026-08-21T10:33:30Z Hariprasad Shetty10 6127 366203 wikitext text/x-wiki {{Short description|ಕೀವ್, ಉಕ್ರೇನ್‌ಡ್ ಇಪ್ಪುನ ಆರ್ಟ್ ನುವೊ ವಾಸ್ತುಶಿಲ್ಪೊದ ಕಟ್ಟಡ}} {{Infobox building | name = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್ | native_name = Будинок з химерами | native_name_lang = uk | image = Будинок із химерами-3.JPG | image_alt = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ದೃಶ್ಯ | image_caption = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ಮುಖಭಾಗ | architectural_style = ಆರ್ಟ್ ನುವೊ | structural_system = ಕಾಂಕ್ರೀಟ್ ಪೈಲುಲು; ನಿರಂತರ ಅಡಿಪಾಯ | location = ಲಿಪ್ಕಿ, ಕೀವ್, ಉಕ್ರೇನ್ | address = 10, ಬ್ಯಾಂಕೋವಾ ಬೀದಿ | coordinates = {{coord|50|26|42|N|30|31|43|E|type:landmark_region:UA-30|display=inline,title}} | construction_start_date = 1901 | completion_date = 1902 | floor_count = 3 (ಬ್ಯಾಂಕೋವಾ ಬೀದಿ ಬದಿಟ್)<br>6 (ಫ್ರಾಂಕೊ ಚೌಕ ಬದಿಟ್) | floor_area = {{convert|3309.5|m2|sqft|2|abbr=on}} | architect = ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರಡೆಟ್ಸ್ಕಿ }} '''ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್''' ಅತ್ತಂದೆ '''ಹೊರೊಡೆಟ್ಸ್ಕಿ ಹೌಸ್''' (ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ಪುದರ್ ಡ್ ಪುದರ್ ದೀತಿನ) ಆರ್ಟ್ ನೌವಿಯೊ ಕಟ್ಟಡ ಉಂದು. ಲಿಪ್ಕಿ ಕಿವ್ ದ ನೆರೆಹೊರೆ, ಉಕ್ರೇನ್ ದೇಶದ ರಾಜಧಾನಿ. ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆನ ಕಚೇರಿದ ಬೀದಿದ ಎದುರುಡು ಉಪ್ಪುನ ಈ ಕಟ್ಟಡೊನು 2005 ಡ್ದ್ ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಅಧ್ಯಕ್ಷೆರೆನ ನಿವಾಸೊ ಆದ್ ಬಳಕೆ ಮಲ್ತೊಂದು ಬೈದೆರ್.<ref name="vr-340">{{Cite Ukrainian law|type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=340|law=340-2005-п|name=On the addition to the house on 10, Bankova St. in the city of Kyiv the status of a government residence|date=2005-05-11}}</ref><ref name="BBC Ukrainian">{{cite news|url=https://www.bbc.com/ukrainian/indepth/story/2005/04/050401_ukraine_residences |title=The aura is fine, but the place is too small|access-date=2006-09-30|first=Marta|last=Shokalo|date=2005-04-01|publisher=[[BBC News|BBC Ukrainian]]|language=uk}}</ref> ಕಟ್ಟಡೊದ ಎದುರುದ ಬೀದಿನ್ ಮಾತಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ಗ್ ಮುಚ್ಚಿದ್ ದೀತೆರ್, ಬೊಕ್ಕ ಇತ್ತೆ ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡಗ್ ಸಮೀಪೊಡು ಉಪ್ಪುನೆರ್ದಾತ್ರ ಉಂದು ಒಂಜಿ ಗಸ್ತು ಪಾದಚಾರಿ ವಲಯ ಆದುಂಡು. ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ಮೂಲತಃ 1901-1902 ಡ್ ತನ್ನ ಸ್ವಂತ ಅಪ್ಮಾರ್ಕೆಟ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಕಟ್ಟಡೊ ಆದ್ ಬಳಕೆ ಮಲ್ಪುನ ನಿಟ್ಟಿಡ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಆಂಡಲಾ, ವರ್ಷೊಲು ಕರಿಲೆಕನೆ, ಹೊರೊಡೆಕಿ ಕಡೆಕ್ ಆರ್ಥಿಕ ತೊಂದರೆಲೆಡ್ದ್ ಕಟ್ಟಡೊನು ಮಾರಾಟ ಮಲ್ಪೊಡಾಂಡ್, ಅಯಿಡ್ದ್ ಬೊಕ್ಕ 2000 ದ ಸುರುತ ಮುಟ್ಟ ಉಕ್ರೇನ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಅಧಿಕೃತ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಕ್ರಮಣ ಮಲ್ಪುನ ದುಂಬು ಉಂದು ಅನೇಕ ಸರ್ತಿ ಮಾಲೀಕತ್ವೊನು ಬದಲಾವಣೆ ಮಲ್ತ್ಂಡ್.<ref name="riddle"/> ಕಟ್ಟಡೊನು ಖಾಲಿ ಮಲ್ತಿನಗ, ಅಯಿತ ಒಳಾಂಗಣ ಬೊಕ್ಕ ಪಿದಯಿದ ಅಲಂಕಾರೊನು ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆನ್ ಅನುಸರಿಸಾದ್ ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆ ಮಲ್ತೆರ್.<ref name="mirtu"/> ಹೊರೊಡೆಕಿ ಒರಿ ಉತ್ಸಾಹಿ ಬೇಟೆಗಾರೆ ಆಯಿನೆಡ್ದಾವರ, ಇಟಾಲಿಯನ್ ವಾಸ್ತುಶಿಲ್ಪಿ ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ಶಿಲ್ಪಕಲೆ ಮಲ್ತಿನ ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲೆನ್ ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲೆನ್ ಚಿತ್ರಿಸವುನ ಅಲಂಕಾರಿಕ ಅಲಂಕಾರೊಲೆಡ್ದ್ ಈ ಕಟ್ಟಡೊಗು ಜನಪ್ರಿಯ ಪುದರ್ ಬತ್ತ್ಂಡ್.<ref name="vinnytsia_library"/> ಈ ಪುದರ್ ಪುರಾಣೊದ ಚಿಮೆರಾ (ಪುರಾಣ)ನ್ ಸೂಚಿಸವುಜಿ, ಆಂಡ ಚಿಮೆರಾ (ವಾಸ್ತುಶಿಲ್ಪ) ಪನ್ಪಿನ ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿನ್ ಸೂಚಿಸವುಂಡು, ಉಂದೆಟ್ ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಒಂಜಿ ಕಟ್ಟಡೊಗು ಅಲಂಕಾರಿಕ ಅಂಶೊಲೆನ ರೂಪೊಡು ಅನ್ವಯಿಸವೆರ್. ಹೊರೊಡೆಕಿನ ಅನನ್ಯ ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿ ಕೀವ್ ದ ಆಂಟೋನಿ ಗೌಡಿ ಪಂಡ್ದ್ ಮೆಚ್ಚುಗೆನ್ ಗಳಸೊಂಡು.<ref name="mirtu">{{harvnb|Ivanenko|2004}}</ref><ref name="element">{{cite news|url=http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |title=heed the krai |access-date=2006-09-30 |first=Sonya |last=Rinkus |work=elementmoscow.ru |url-status=dead |archive-url=https://web.archive.org/web/20071109061542/http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |archive-date=2007-11-09 }}</ref> ==ಇತಿಹಾಸ== ===ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಸುರುತ ಇತಿಹಾಸ=== ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಒಂಜಿ ಇಲ್ಲ್ ನ್ ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ೧೯೦೧-೧೯೦೨ಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್.<ref name="mirtu"/> ಹೊರೊಡೆಕಿ ೧೮೬೩ಡ್ ಪೊಡೊಲಿಯಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಸಮೃದ್ಧ ಪೋಲಿಷ್ ಜನಕುಲು ಸ್ಜ್ಲಾಚ್ಟಾ (ಉತ್ತಮ) ಕುಟುಂಬೊಡು ಪುಟ್ಟುವೆರ್.<ref name="vinnytsia_library">{{cite web|url=http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |title=Architector of century |date=2003-05-20 |access-date=2007-07-14 |publisher=Vinnytsia oblast' universal science library named after K.A.Timiryazev |language=uk |archive-url=https://web.archive.org/web/20070928062123/http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |archive-date=September 28, 2007 |url-status=dead }}</ref> ೧೮೯೦ಡ್ ಇಂಪೀರಿಯಲ್ ಅಕಾಡೆಮಿ ಆಫ್ ಆರ್ಟ್ಸ್ ನ್ ಸೈಂಟ್ ಪೀಟರ್ಸ್ಬರ್ಗ್ ಮುಗಿತ್ ಬೊಕ್ಕ, ಆರ್ ಕೀವ್ ಗ್ ಪೋಯೆರ್, ಅಲ್ಪ ಆರ್ ಸುಮಾರ್ ೩೦ ವರ್ಸೊಗು ಜೀವನ ಮಲ್ತೆರ್.<ref name="vinnytsia_library"/> ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಹೊರೊಡೆಕಿ ಒರಿ ಪ್ರಮುಖ ಕೀವ್ ವಾಸ್ತುಶಿಲ್ಪಿಯಾದ್ ತನ್ನನ್ ತಾನ್ ಸ್ಥಾಪಿಸೊಂದಿತ್ತೆರ್, ಅರೆನ ನಿಕಟ ಸ್ನೇಹಿತೆ ಬೊಕ್ಕ ಪಾಲುದಾರ ಎಂಜಿನಿಯರ್ ಆಂಟನ್ ಸ್ಟ್ರಾಸ್ ನೊಟ್ಟುಗು ಮಸ್ತ್ ನಗರ ಕಟ್ಟಡೊಲೆನ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್, ಸೆಂಟ್. ನಿಕೋಲಸ್ ರೋಮನ್ ಕ್ಯಾಥೊಲಿಕ್ ಕ್ಯಾಥೆಡ್ರಲ್, ಕೀವ್ ಡ್ದ್ ಕರೈಟೆ ಕೆನೆಸಾ (ಕೀವ್) ಬೊಕ್ಕ ಇನಿ ಉಕ್ರೇನ್ ದ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ಮ್ಯೂಸಿಯಂ. ವಾಸ್ತುಶಿಲ್ಪೊದ ಜೊತೆಗೆ, ಹೊರೊಡೆಕಿಗ್ ಮಲ್ಲ-ಆಟದ ಬೇಟೆ ಡ್ ಆಸಕ್ತಿ ಇತ್ತ್ಂಡ್,<ref name="vinnytsia_library"/> ಉಂದು ಅರೆನ ಕಟ್ಟಡೊಡು ದಾಯೆಗ್ ಮಸ್ತ್ ಪ್ರಾಣಿಲೆನ್ ತೋಜಾವುಂಡು ಪನ್ಪಿನೆನ್ ವಿವರಿಸವುಂಡು. [[File:House with Chimaeras during construction.jpg|thumb|left|ನಿರ್ಮಾಣೊಡು ಉಪ್ಪುನ ಕಟ್ಟಡ, ೧೯೦೨]] ಹೊರೊಡೆಕಿ ಸಾಲದ ದುಡ್ಡುಡ್ದ್ ಇಲ್ಲ್ ದ ನಿರ್ಮಾಣೊಗು ಹಣಕಾಸು ಮಲ್ತೆರ್,<ref name="mirtu"/><ref name="KV"/> ಉಂದು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಆಯೆರೆ ಉದ್ದೇಶೊಡು.<ref name="Horodetskyi">{{harvnb|Malakov|1999|p=?}}</ref><ref name="Touring Kyiv">{{harvnb|Malikenaite|2003|p=50.}}</ref> ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಲಾ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ರೂಪಿಸೊಂದಿತ್ತ್ಂಡ್, ಉಂದೆನ್ ಎಲಿವೇಟರ್] ಬೊಕ್ಕ ಮೆಟ್ಟಿಲುಲೆಡ್ದ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್.{{refn|ಉಂದೆಕ್ ಒಂಜೇ ಅಪವಾದ ಪಂಡ ಕಟ್ಟಡೊದ ಕೆಳಮಹಡಿಡ್ ರಡ್ಡ್ ಬೇತೆ ಬೇತೆ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್.|group=nb}} ಹೊರೊಡೆಕಿ ಸ್ವತಃ ಕಟ್ಟಡದ ನಾಲ್ನೇ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ತೆರ್, ಸುಮಾರ್{{convert|380|m2|sqft|abbr=on}}.<ref name="Horodetskyi"/>{{refn|ಅರೆನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಸೂಕ್ತವಾಯಿನವು. ನಂ. 3, ಮುಖ್ಯ ಮಹಡಿ, ಬಂಕೋವಾ ಬೀದಿದ ಮಟ್ಟೊಡ್ದ್ ತೂಂಡ.|group=nb}} ಹೊರೊಡೆಕಿ ಫೆಬ್ರವರಿ ೧, ೧೯೦೧ಡ್ ಸುರುತ ಜಮಿನ್ ನ್ ಕೊಂಡೊಂತೆರ್, ಆ ವರ್ಷದ ಮಾರ್ಚ್ ೧೮ಡ್ ನಿರ್ಮಾಣದ ಬೇಲೆ ಸುರು ಆಂಡ್. ಪಿದಯಿದ ಗೋಡೆಲೆನ ನಿರ್ಮಾಣ ಆಗಸ್ಟ್ ೨೧ ಗ್ ಮುಗಿಂಡ್, ಬೊಕ್ಕ ಛಾವಣಿನ್ ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಮಾತಾ ಕಲ್ಲುದ ಬೇಲೆನ್ ಸೆಪ್ಟೆಂಬರ್ ೧೩ ಗ್ ಮುಗಿತ್ಂಡ್.<ref name="Horodetskyi"/><ref name="Touring Kyiv"/> ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ದ ಒಳಗೆ ಆರ್ಥಿಕ ಕಷ್ಟೊಲೆಡ್ದ್, ಕಟ್ಟಡೊದ ಪೂರ್ಣಗೊಳಿಸುವಿಕೆ ತಡವಾಂಡ್. ಮೇ ೧೯೦೩ಡ್, ಕೆಳಮಟ್ಟೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಬೊಕ್ಕ ಹೊರೊಡೆಕಿನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಮಾತ್ರ ಆಕ್ರಮಣ ಮಲ್ತೆರ್.<ref name="Horodetskyi"/> ಭೂಮಿ ಬೊಕ್ಕ ನಿರ್ಮಾಣದ ಒಟ್ಟು ವೆಚ್ಚ 133,000 ಆಂಡ್ ರಷ್ಯನ್ ರೂಬಲ್.{{refn|ಈ ಬೆಲೆ ೧೯೦೩ ಡ್ದ್ ಉಂಡು.[https://web.archive.org/web/20170323182358/http://www.archives.gov.ua/Archives/index.php?ra27 Government Archive of the City of Kyiv] – [[Fonds|Fond]] No. 143, Series 2, File No. 520, Item No. 9. In the late 19th and early 20th century, one thousand [[Russian ruble]]s were worth about 24.89 [[gold]] [[troy ounce]]s, or roughly one million Russian rubles as of 2011.{{sfn|Malakov|1999}}|group=nb}} ಒಟ್ಟುಗು, {{convert|1550|m2|sqft|abbr=on}} ಜಮಿನ್ ನ್ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಗು ಬಳಕೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಒಟ್ಟು 15,640 ರೂಬಲ್ಸ್ ಖರ್ಚು ಆಂಡ್.<ref name="KV">{{cite web|url=http://old.kv.com.ua/index.php?rub=46&number_old=2984|title=The rebirth of the House with Chimaeras|access-date=2006-07-14|first=Mikhail|last=Kalnitsky|work=Kievskie Vedomosti №179 (2984)|language=ru|date=2003-08-19|archive-date=2018-08-20|archive-url=https://web.archive.org/web/20180820045803/http://old.kv.com.ua/index.php?rub=46&number_old=2984|url-status=dead}}</ref> ಬಾಡಿಗೆಡ್ದ್ ವಾರ್ಷಿಕ ಲಾಭ 7,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್. ಹೊರೊಡೆಕಿ ತಾಜಾ ಇನ್-ಹೌಸ್ ಹಾಲುದ ಒತ್ತಾಯೊಗು ಕಾರಣವಾದ್ ಒಂಜಿ ಗೋಕುಲೊನು ಆವರಣೊಡು ದೀಯೆರ್,<ref name="mirtu"/><ref name="Touring Kyiv"/> ಗಾಯೊಲೆನ ವಾಸನೆ ಬಾಡಿಗೆದಾರೆರೆಗ್ ತೊಂದರೆ ಆವಂದಿಲೆಕ ನಿರ್ದಿಷ್ಟವಾದ್ ದೀದ್ ಇತ್ತ್ಂಡಲಾ. ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಒಂಜಿ ಜಾಗೆಡ್, ಒಂಜಿ ಎಲ್ಯ ಆಲ್ಪೈನ್ ಗಾರ್ಡನ್ (ಅಂದಾಜಿ. {{convert|320|m2|sqft|-2|abbr=on|disp=or}}) ಬೊಕ್ಕ ಒಂಜಿ ಫೌಂಟೇನ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರೆನ ಸಫಾರಿ ಬೇಟೆದ ಹವ್ಯಾಸೊನು ಒಳಗೊಂಡ ಆರ್ಥಿಕ ದುರ್ಬಳಕೆಡ್ದ್,<ref name="mirtu"/> ಜುಲೈ ೧೯೧೨ಡ್, ಹೊರೊಡೆಕಿ ಕೀವ್ ಮ್ಯೂಚುಯಲ್ ಕ್ರೆಡಿಟ್ ಅಸೋಸಿಯೇಷನ್ ​​ಡ್ದ್ ದೆತೊನುನ ಸಾಲೊಗು ವಿರುದ್ಧವಾದ್ ಕಟ್ಟಡೊನು ಜಾಮೀನು ಆದ್ ವಾಗ್ದಾನ ಮಲ್ತೆರ್.<ref name="riddle">{{cite web|title=Riddle of House with Chimaeras|url=http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|first=Yuliya|last=Ivashko|work=Vash Kiev|access-date=2006-07-14|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20120206003140/http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|archive-date=2012-02-06}}</ref> ಹೊರೊಡೆಕಿ ಸಾಲೊಡು ಡಿಫಾಲ್ಟ್ ಆನಗ, ಕಟ್ಟಡೊನು ೧೯೧೩ಡ್ ಹರಾಜು ಮಲ್ತೆರ್,<ref name="riddle"/> ಬೊಕ್ಕ ಎಂಜಿನಿಯರ್ ಡೇನಿಯಲ್ ಬಾಲಾಖೊವ್ಸ್ಕಿನ ಆಸ್ತಿ ಆಂಡ್,<ref name="mirtu"/> ಕೀವ್ ವ್ಯಾಪಾರಿನ ಮಗೆ,<ref name="KV"/> ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆದ ನಿರ್ದೇಶಕ ಮಂಡಳಿದ ಅಧ್ಯಕ್ಷೆರಾದಿತ್ತೆರ್,<ref name="KV"/> ಬೊಕ್ಕ ಕೀವ್ ಡ್ ಒರಿ ಫ್ರಾನ್ಸ್/ಫ್ರೆಂಚ್ ಕಾನ್ಸುಲಾರ್ ಏಜೆಂಟ್.<ref name="Fyodorovskaya">{{cite web|url=http://www.house-ua.com/news395.html |title=Hunter from the House with Chimaeras |access-date=2006-07-14 |first=Olga |last=Fyodorovskaya |work=Ukrainskiy Dom |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20070929175434/http://www.house-ua.com/news395.html |archive-date=September 29, 2007 }}</ref> ೧೯೧೬ಡ್, ಈ ಇಲ್ಲ್ ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆಗ್ ಸೇರ್ದಿನವು.<ref name="riddle"/> ೧೯೧೮ಡ್, ಕಟ್ಟಡೊದ ಮಾಲೀಕತ್ವ ಮತ್ತೆ ಬದಲಾಂಡ್, ಸ್ಯಾಮ್ಯುಯೆಲ್ ನೆಮೆಟ್ಸ್ ಗ್.<ref name="riddle"/> ೧೯೨೧ಡ್, ಬೊಲ್ಶೆವಿಕ್ ಲು ಕೀವ್ ನ್ ನಿಯಂತ್ರಣ ಮಲ್ತಿನ ಬುಕ್ಕೊ, ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್/ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್ ದ ಕೆಲವು ಇಲಾಖೆಲು ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಸದನೊಡು ಕಚೇರಿಲೆನ್ ದೆತೊನುವೆರ್. ===ಒಡೆತನ ೧೯೨೧–೨೦೦೨=== ರಷ್ಯಾದ ಕ್ರಾಂತಿದ ನಂತರದ ಉಕ್ರೇನ್/ಅಶಾಂತಿದ ಅವಧಿ ರಷ್ಯಾದ ಕ್ರಾಂತಿ (೧೯೧೭) ನಂತರ, ಕಟ್ಟಡೊನು ರಾಷ್ಟ್ರೀಕರಣ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನನೊಂಜಿ ಸರ್ತಿ ಕಮ್ಯುನಲ್ಕಾ/ಸಾಮುದಾಯಿಕ ಜೀವನಗ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="president.gov.ua">{{cite web|url=http://www.president.gov.ua/en/slideshow/3.html |title=House with Chimeras |access-date=2008-03-20 |work=[[President of Ukraine|Official web-site of President of Ukraine]] |url-status=dead |archive-url=https://web.archive.org/web/20080425002336/http://www.president.gov.ua/en/slideshow/3.html |archive-date=2008-04-25 }}</ref> ಪ್ರತಿಯೊಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಸುಮಾರ್ ಒಂಜಿ ರ್ದ್ ಪತ್ತ್ ಕುಟುಮೊಲು ನೆಲೆಯಾದಿತ್ತೆರ್.<ref name="Horodetskyi"/> ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧ (೧೯೪೧-೧೯೪೩) ದ ಪೊರ್ತುಡು, ಈ ಕಟ್ಟಡೊನು ಬುಡ್ದು ಪೋಯೆರ್. ಯುದ್ಧದ ಸಮಯೊಡು ಕಠಿಣ ಅಂಶೊಲೆಗ್ ಒಡ್ಡಿಕೊಳ್ಳುನ ಕಾರಣ, ಕಟ್ಟಡೊಗು ಅಯಿತ ರಚನೆಗ್ ಮಸ್ತ್ ಹಾನಿ ಆಂಡ್.<ref name="Horodetskyi"/> ಯುದ್ಧೊದ ನಂತರ, ಈ ಕಟ್ಟಡೊನು ಇವಾನ್ ಫ್ರಾಂಕೊ ರಾಷ್ಟ್ರೀಯ ಶೈಕ್ಷಣಿಕ ನಾಟಕ ರಂಗಮಂದಿರಡ್ದ್ ಸ್ಥಳಾಂತರಿಸಾಯಿನ ನಟೆರೆಗ್ ನಿವಾಸವಾದ್ ಸಂಕ್ಷಿಪ್ತವಾದ್ ಬಳಕೆ ಮಲ್ತೆರ್;<ref name="KV"/> ಆಂಡಲಾ, ಉಕ್ರೇನಿಯನ್ ಸೋವಿಯೆತ್ ಸಮಾಜವಾದಿ ಗಣರಾಜ್ಯೊದ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಫ್ ಉಕ್ರೇನ್ ದ ಕೇಂದ್ರ ಸಮಿತಿ ಈ ಕಟ್ಟಡೊದ ಒಡೆತನೊನು ದೆತೊನುಂಡು ಬೊಕ್ಕ ಅಯಿನ್ ಅಕ್ಲೆನ ಗಣ್ಯೆರೆಗ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ನಂಬರ್ ೧ ಆದ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="Horodetskyi"/> ಪಾಲಿಕ್ಲಿನಿಕ್ 20ನೆ ಶತಮಾನೊದ ಅಂತ್ಯೊಗು ಮುಟ್ಟ ಈ ಕಟ್ಟಡೊನು ಬಳಕೆ ಮಲ್ತ್ಂಡ್. ಆ ಸಮಯೊಡು, ಕಟ್ಟಡ ಬಹುತೇಕ ಅರ್ಧೊಗು ಪುಡಾಂಡ್. ಒಂಜಿ ಭಾಗ {{convert|22|cm|in|0|abbr=on}} ನ್ ಸಾಗ್ ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ ಒಂಜಿ ಪ್ರಮುಖ ಲಂಬ ಬಿರುಕು ರೂಪುಗೊಂಡುಂಡು, ಉಂದೆತ ಅಗಲ ಸುಮಾರ್ {{convert|40|cm|in|0|abbr=on}} ಉಂಡು.<ref name="Horodetskyi"/> ಕಟ್ಟಡೊದ ಕೆಲವು ವಾಸ್ತುಶಿಲ್ಪದ ವಿವರೊಲು ಒಂಜಿ ವೇಳೆ ಚಿಪ್ ಆತ್ಂಡ್, ಅತ್ತ್ಂಡ ಬಿರುಕು ಬುಲೆದ್ಂಡ್. ಕಟ್ಟಡೊದ ಜೀರ್ಣೋದ್ಧಾರದ ಬೇಲೆನ್ 2002 ಗ್ ನಿಗದಿ ಮಲ್ತೆರ್, ಆಂಡಲಾ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಆಪರೇಟರ್ ಲು 40 ವರ್ಸೊಡ್ದ್ ಜಾಸ್ತಿ ಕಟ್ಟಡೊನು ಆಕ್ರಮಣ ಮಲ್ತ್ ದ್ ಬುಡುದು ಪೋಯೆರೆ ಹಿಂಜರಿಯೊಂದಿತ್ತೆರ್. ಕಟ್ಟಡೊಡ್ದ್ ನಿವಾಸಿಲೆನ್ ಪಿದಯಿ ಕನರೆ, ಕೆಲಸದಕುಲು ಮಾತಾ ಕಿಟಕಿಲೆನ್ ಮುಚ್ಚಿಯೆರ್ ಬೊಕ್ಕ ಪಾಲಿಕ್ಲಿನಿಕ್ ಆವರಣೊನು ಖಾಲಿ ಮಲ್ತ್ಜಿಂಡ ಬಾಗಿಲುಲೆನ್ ಲಾ ಅಂಚನೆ ಮಲ್ಪುವೆ ಪಂದ್ ಬೆದರಿಕೆ ಮಲ್ತೆರ್. ಈ ವಿಷಯೊಡು ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ನ ಪಾಲ್ ಪಡೆಯಿನೆಡ್ದಾವರ ಮಾತ್ರ ಪಾಲಿಕ್ಲಿನಿಕ್ ನ್ ಸಂಪೂರ್ಣವಾದ್ ಪಿದಯಿ ಬುಡ್ಪಾಯೆರ್.<ref name="mirtu"/> ===ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಅಧಿಕೃತ ಬಳಕೆ=== [[File:Flags of EU and Ukraine.jpg|thumb|ಒಂಜಿ ಅಧಿಕೃತ ಕಾರ್ಯಕ್ರಮೊದ ಪೊರ್ತುಡು ಕಟ್ಟಡೊದ ಎದುರು ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪಿಯನ್ ಒಕ್ಕೂಟದ ಧ್ವಜೊಲು]] ಜೀರ್ಣೋದ್ಧಾರದ ಸಮಯೊಡು, ''ಪ್ರಾಜೆಕ್ಟ್ ರೆಸ್ಟಾವ್ರಾಟ್ಸಿಯಾ'' ನಡಪಾಯಿನ ಬೊಕ್ಕ ನಟಾಲಿಯಾ ಕೊಸೆಂಕೊ ಮುಖ್ಯಸ್ಥೆರಾದ್,<ref name="vr-829-р">{{Cite Ukrainian law |type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=829-р|law=829-2003-р |name=On the recognition of the project and the company carrying out the building's reconstruction as well as the restoration of the monument of architecture and the government monument of the city on Bankova St., 10 in the city of Kyiv|date=2003-12-31}}</ref> ಕಟ್ಟಡದ ಅಡಿಪಾಯೊನು ಬಲಪಡಾಯೆರೆ ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟ ಕಾಲೊಡು ತುಂಬಿನ ಇಡೀ ಕೆಳಮಹಡಿನ್ ಕೆಲಸದಕುಲು ಕಂಡು ಕನತೆರ್.<ref name="mirtu"/> ಒಳಾಂಗಣದ ವಿಸ್ತಾರವಾದ್ ಅಲಂಕಾರೊನು ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪೊಡಾಂಡ್. ಅಂಗಳೊಡು, ಪುನಃಸ್ಥಾಪಕೆರ್ ಒಂಜಿ ಕೃತಕ ಕೆರೆ, ಕಾರಂಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಎಲ್ಯ ಉದ್ಯಾನೊನು ದೀಯೆರ್—ಉಂದು ಮಾತ ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆಡ್ ಇತ್ತ್ಂಡ್.<ref name="mirtu"/> ಈ ಕಟ್ಟಡೊನು ನವೆಂಬರ್ ೨೦೦೪ಡ್ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ "ಉಕ್ರೇನಿಯನ್ ಕಲೆತ ಮೇರುಕೃತಿಲು" ಆದ್ ತೆರೆಯೆರ್.<ref name="museum">{{cite news|url=http://www.korespondent.net/main/31543 |title=A cultural museum center opens in the House with Chimaeras |access-date=2007-07-12 |date=2004-11-19 |work=[[Korrespondent]] |publisher=Bigmir-Internet |language=uk |archive-url=https://web.archive.org/web/20070927234156/http://www.korespondent.net/main/31543 |archive-date=September 27, 2007 |url-status=dead }}</ref> ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಆದ್ ಬೊಕ್ಕ ರಾಜ್ಯೊದ ಸಂದರ್ಶಕೆರೆಗ್ ಅಧ್ಯಕ್ಷೀಯ ಸಭೆತ ಜಾಗೆ ಆದ್ ದ್ವಿಗುಣ ಉದ್ದೇಶೊಗು ಸೇವೆ ಮಲ್ಪುಂಡು ಪನ್ಪಿನ ನಿರೀಕ್ಷೆ ಇತ್ತ್ಂಡ್. ಏಪ್ರಿಲ್ ೨೦೦೫ಡ್, ಕೀವ್ ಸಿಟಿ ಕೌನ್ಸಿಲ್ ಒಂಜಿ ಮಸೂದೆನ್ ಸಲ್ಲಿಸಯೆರ್<ref>[http://www.kmr.gov.ua/decree.asp Decrees of Kyiv City Council] {{webarchive|url=https://web.archive.org/web/20070707053720/http://kmr.gov.ua/decree.asp |date=2007-07-07 }} [[Kyiv City Council|Kyiv City Council decree]] No. '''380/2955''': [http://www.kmr.gov.ua/decree.asp?Id=2235 On the allowance of the change of ownership to the government authority with permission of conducting reconstruction and restoration works on the building of 10, Bankova Street] {{webarchive|url=https://web.archive.org/web/20070927225321/http://www.kmr.gov.ua/decree.asp?Id=2235 |date=2007-09-27 }}. Passed on 2005-04-21. {{in lang|uk}}</ref> [[ಉಕ್ರೇನಿಯನ್ ಹೈವ್ನಿಯಾ|₴]]104 ಮಿಲಿಯನ್ (ಅಂದಾಜಿ ಯುನೈಟೆಡ್ ಸ್ಟೇಟ್ಸ್ ಡಾಲರ್/ 20 ಮಿಲಿಯನ್) ಗ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರಗ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮನೆತ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆಗ್.<ref name="proUA">{{cite news|url=http://ua.proua.com/news/2005/05/11/180559.html|archive-url=https://web.archive.org/web/20070930044045/http://ua.proua.com/news/2005/05/11/180559.html|url-status=dead|archive-date=2007-09-30|title="House with Chimaeras" received the status of an official governmental residence|access-date=2006-09-30|date=2004-11-19|work=proUA|language=uk}}</ref> ಅಧಿಕೃತ ಸಮಾರಂಭೊಲೆಡ್ ಬಳಕೆ ಮಲ್ಪುನ ಕಟ್ಟಡೊದ ಎದುರುಡು ಒಂಜಿ ಪೊಸ ಚೌಕೊನು (ಎಲ್ಲಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ನ್ ಮುಚ್ಚುನ) ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆಲಾ ಕೌನ್ಸಿಲ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರೊಗು ಅನುಮತಿ ಕೊರ್ತುಂಡು.<ref name="proUA"/> ಮೇ ೨೦೦೫ಡ್ದ್, ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಅಧಿಕೃತ ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆರ್ನ| ನಿವಾಸ ಆದುಂಡು, ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಉಪಯೋಗ ಆಪುಂಡು.<ref name="vr-340"/><ref>{{Cite web |title=Ukrainian architecture: 10 buildings that everyone should see |url=https://rubryka.com/en/article/architecture-of-ukraine/}}</ref><ref name="BBC Ukrainian"/> ದಿ ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್ ನ್ ಉಕ್ರೇನಿಯನ್ ಅಧ್ಯಕ್ಷೆರ್ ವಿಕ್ಟರ್ ಯುಶ್ಚೆಂಕೊ ಬೊಕ್ಕ ರಷ್ಯಾದ ಅಧ್ಯಕ್ಷೆರ್ ವ್ಲಾಡಿಮಿರ್ ಪುಟಿನ್ ನಡುಟು ಒಂಜಿ ಸಭೆತ ಜಾಗೆ ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್, ಅಪಗ ಚಿಮೇರಾಸ್ ಡಿಸೆಂಬರ್ ೨೨, ೨೦೦೬ಡ್ ಕೀವ್ ಗ್ ಭೇಟಿ ಕೊರ್ತೆರ್.<ref>{{cite news|url=http://www.korrespondent.net/main/174172 |title=Yushchenko and Putin decided their goals for two years ahead|access-date=2007-07-12|date=2006-12-22|work=[[Korrespondent]]|publisher=Bigmir-Internet|language=ru}}</ref> ಕಟ್ಟಡೊಡು ಮಾತುಕತೆ, ಪಾತೆರ, ಅಧಿಕೃತ ದಾಖಲೆಲೆಗ್ ಸಹಿ ಪಾಡುನ ಕೊಠಡಿಲು, ಅಂಚನೆ ಪತ್ರಿಕೆಲೆಗ್ ವಿಶೇಷ ಕೊಠಡಿ ಸೇರ್ದ್ ಉಂಡು.<ref name="president.gov.ua"/> ==ವಾಸ್ತುಶಿಲ್ಪ== [[File:Architectural details on House with Chimaeras 2007.JPG|thumb|250px|ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ವಿನ್ಯಾಸಗೊಳಿಸಾಯಿನ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಉಭಯಚರೊಲು ಬೊಕ್ಕ ಬೇತೆ ಜೀವಿಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರತಿಮೆಲೆನ ವಿವರವಾದ್ ನೋಟ]] {| cellpadding="1" style="float:left; border:1px solid #8888aa; padding:5px; font-size:95%; margin:0 15px 0 0; background:#f9f9f9; width:200px;" |- | architectural_style=" text-align: center;" |ವೊಲೊಡಿಮಿರ್ ಯಾಸಿಯೆವಿಚ್ <ref name="wood">{{harvnb|Yasiievych|1988|p=93.}}</ref> |- |<small>ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಇಲ್ಲ್ ನ್ ಆಂಟೋನಿ ಗೌಡಿ ನ ಕೆಲಸೊಲೆನೊಟ್ಟುಗು ಮಾತ್ರ ಹೋಲಿಕೆ ಮಲ್ಪೊಲಿ, ವಿಶೇಷವಾದ್ ಬಾರ್ಸಿಲೋನಾ, ಸ್ಪೇನ್ ಡ್ ಪ್ರಸಿದ್ಧ ಕಾಸಾ ಮಿಲಾ, ಒಂಜಿ ರಡ್ಡ್ ವರ್ಷೊಡ್ದು ಬೊಕ್ಕ (೧೯೦೫-೧೯೧೦) ನಿರ್ಮಾಣ ಆಯಿನ ನಡುಟು.</small> |} ಈ ಕಟ್ಟಡೊನು ಆರ್ಟ್ ನೌವಿಯೊ ಶೈಲಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಉಂದು ಆ ಕಾಲೊಡು ತುಲನಾತ್ಮಕವಾದ್ ಪೊಸ ಶೈಲಿ ಆದಿತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಹರಿಯುನ, ವಕ್ರರೇಖೆದ ವಿನ್ಯಾಸೊಲೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್, ಉಂದೆಟ್ ಮಸ್ತ್ ಸರ್ತಿ ಪುಷ್ಪ ಬೊಕ್ಕ ಬೇತೆ ಸಸ್ಯ-ಪ್ರೇರಿತ ಮೋಟಿಫ್ ಲೆನ್ ಸೇರಾವೊಂದಿತ್ತೆರ್. ಹೊರೊಡೆಕಿ ಕಟ್ಟಡದ ಪಿದಯಿದ ಅಲಂಕಾರೊಡು ಪೌರಾಣಿಕ ಜೀವಿಲೆನ ಬೊಕ್ಕ ಮಲ್ಲ ಆಟದ ಪ್ರಾಣಿಲೆನ ರೂಪೊಡು ಇಂಚಿನ ಮೋಟಿಫ್ ಲೆನ್ ತೋಜಾದ್ ಕೊರ್ತೆರ್. ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮಲ್ತಿನ ಇಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಬೇಲೆಡ್ದ್ ಕೀವ್ ದ ಆಂಟೋನಿ ಗೌಡಿ ಪನ್ಪಿನ ಅಡ್ಡ ಪುದರ್ ನ್ ಗಳಸೊಂಡೆರ್.<ref name="mirtu"/><ref name="element"/> ಕಟ್ಟಡ ಉಪ್ಪುನ ಕಡಿಮೆ ಇಳಿಜಾರುಡ್ದ್, ಅಯಿತ ಅಡಿಪಾಯೊಗು ಸರಿಯಾದ್ ಸರಿ ಆಯೆರೆ ಕಾಂಕ್ರೀಟ್ ಡ್ದ್ ವಿಶೇಷವಾದ್ ವಿನ್ಯಾಸಗೊಳಿಸೊಡಾಪುಂಡು. ಎದುರುಡ್ದ್ ತೂನಗ, ಕಟ್ಟಡೊಗು ಮೂಜಿ ಮಹಡಿಲು ಮಾತ್ರ ಉಪ್ಪುನಂಚ ತೋಜುಂಡು. ಆಂಡಲಾ, ಪಿರವುಡ್ದ್, ಅಯಿತ ಆಜಿ ಮಹಡಿಲೆನ್ ತೂವೊಲಿ.<ref name="vlasenko">{{harvnb|Zharikov|1983–1986|p=38.}}</ref>ಕಟ್ಟಡದ ಅಡಿಪಾಯೊದ ಒಂಜಿ ಭಾಗೊನು ಕಾಂಕ್ರೀಟ್ ರಾಶಿಲೆಡ್ ಮಲ್ತೆರ್, ಬೊಕ್ಕೊಂಜಿ ಭಾಗೊನು ನಿರಂತರ ಅಡಿಪಾಯೊದ ರೂಪೊಡು ಮಲ್ತೆರ್. ಸಾಮಾನ್ಯವಾದ್, ಈ ರಡ್ಡ್ ವಿಧಾನೊಲು ಎಡ್ಡೆ ರೀತಿಡ್ ಮಿಶ್ರಣ ಆಪುಜಿ ಆಂಡ ಹೊರೊಡೆಕಿ ಒಂಜಿ ರೀತಿಡ್ ಈ ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆನ್ ಜಯಿಪುನಗ ಯಶಸ್ವಿಯಾಯೆರ್. ಇಟಾಲಿಯನ್ ಶಿಲ್ಪಿ ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ಆಂತರಿಕ ಬೊಕ್ಕ ಬಾಹ್ಯ ಶಿಲ್ಪಕಲೆತ ಅಲಂಕಾರೊಲೆಗ್ ಜವಾಬ್ದಾರರಾದಿತ್ತೆರ್, ಉದಾಹರಣೆಗೆ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಡಾಲ್ಫಿನ್ ಲು, ಬೊಕ್ಕ ಕಟ್ಟಡದ ಛಾವಣಿಡ್ ಕಪ್ಪೆಲು, ಪಿದಯಿದ ಗೋಡೆಲೆಡ್ ಮುಳುಗಡೆ ಆಪಿನ ನೌಕೆಲು ಬೊಕ್ಕ ಬೇಟೆದ ಟ್ರೋಫಿಲು, ಬೊಕ್ಕ ಒಳಾಂಗಣ ಅಲಂಕಾರೊಲು, ಉದಾಹರಣೆಗೆ ಮೆಟ್ಟಿಲುಲು ಬೊಕ್ಕ ಮಲ್ಲ ವೇಲು ಚಾಂಡೆಲಿಯರ್ಲು ನಿಂಫೆಯಾ/ಕಮಲ ಪುಷ್ಪೊಲೆನ ಕಾಂಡೊಲೆಡ್ ಕಚ್ಚಿದ್ ದೀತಿನ ಮಲ್ಲ ಕ್ಯಾಟ್ ಫಿಶ್ನ್ ಚಿತ್ರಿಸವುಂಡು. ಸಾಲಾ ಮಲ್ತಿನ ಪಿದಯಿದ ಶಿಲ್ಪೊಲು [[ಸಿಮೆಂಟ್]] ಡ್ದ್ ಮಲ್ಪುನವು. ಸಿಮೆಂಟ್ ದ ಉತ್ಪಾದನೆ «ಫಾರ್» ಕಂಪೆನಿಡ್ ಇತ್ತ್ಂಡ್, ಅಯಿಟ್ ಹೊರೊಡೆಕಿ ಸಹ-ನಿರ್ದೇಶಕೆರ್ ಆದಿತ್ತೆರ್.<ref name="klymenko">{{cite web|url=http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|archive-url=https://web.archive.org/web/20050310062635/http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|url-status=dead|archive-date=2005-03-10|title=Budynok z Khimeramy|access-date=2006-09-30|first=Sergiy|last=Klymenko|work=Photos of Kyiv|language=uk, ru}}</ref> ಕಂಪೆನಿದ ಮುಖ್ಯ ನಿರ್ದೇಶಕೆರ್ ರಿಚ್ಟರ್ ನ ಕೋರಿಕೆದ ಪ್ರಕಾರ ಸಿಮೆಂಟ್ ನ್ ಪ್ರಾಥಮಿಕ ಕಟ್ಟಡ ಸಾಮಗ್ರಿ ಆದ್ ಮಾತ್ರ ಬಳಕೆ ಮಲ್ತೆರ್.<ref>{{cite web|url=http://www.narodnapravda.com.ua/travel/468d12ca51334/|title=Walks around Kyiv. House with Chimaeras|access-date=2006-07-14|author=explorer|date=2007-07-05|work=Narodna pravda|language=uk|archive-date=2011-07-25|archive-url=https://web.archive.org/web/20110725230507/http://www.narodnapravda.com.ua/travel/468d12ca51334/|url-status=dead}}</ref>ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಸಿಮೆಂಟ್ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿ ಆದ್ ಜನಪ್ರಿಯ ಆದಿಜ್ಜಿ, ಅಂಚಾದ್ ಅಯಿತ ಬಳಕೆನ್ ಇಲ್ಲ್ ಬೊಕ್ಕ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿಲೆಗ್ ಪ್ರಚಾರೊದ ರೂಪೊಡು ಬಳಕೆ ಮಲ್ತೆರ್.<ref name="klymenko"/> <gallery class="center" widths=250 heights=185> File:Architectural details on House with Chimaeras 2007-2.JPG|ಜಿಂಕೆ ಬೊಕ್ಕ ಖಡ್ಗಮೃಗಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ವಿಸ್ತಾರವಾದ್ ಸಿಮೆಂಟ್ ಅಲಂಕಾರೊಲು File:Elephant on House with Chimaeras.JPG|ಈ ಕಟ್ಟಡೊಡು ಬೇತೆ ಬೇತೆ ಕೆತ್ತನೆಲು ಉಂಡು, ಉದಾಹರಣೆಗೆ ಈ ಆನೆ-ತಲೆತ ಗಾರ್ಗೊಯ್ಲ್ File:Architectural details on House with Chimaeras 2007-3.JPG|ಕಟ್ಟಡೊದ ಸುತ್ತ ತೂಯಿನ ಮಸ್ತ್ ಮೂರ್ತಿಲೆಡ್ ಒಂಜಿ </gallery> ===ನೆಲತ ಯೋಜನೆ=== [[File:House with Chimaeras (Plan).JPG|thumb|ಆಜನೇ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅರೆನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಹೊರೊಡೆಕಿನ ಮೂಲ ನೀಲನಕ್ಷೆಲು; ೧೯೦೦ ದಶಕದ ಸುರುಟು]] ಚಿಮೇರಾಸ್ ನ ಇಲ್ಲ್ ನ್ ಬಾಡಿಗೆದಾರೆರ್ ಇಡೀ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ಪುನಂಚಿನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಡ್ ಖಾಸಗಿ ಅಡುಗೆಮನೆಡ್ದ್ ಎಲ್ಯ ಪುಡಿ ಕೊಠಡಿಲೆಗ್ ಮುಟ್ಟ ಅಗತ್ಯವಾಯಿನ ಮಾತಾ ಇಲ್ಲದ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಕಟ್ಟಡೊದ ಉಲಯಿ ಉಪ್ಪುನ ತೆರೆದ ನೆಲತ ಯೋಜನೆ ಬೊಕ್ಕ ಹೆಚ್ಚುವರಿ ಕೊಠಡಿಲು 20ನೆ ಶತಮಾನೊದ ಸುರುತ ಶ್ರೀಮಂತೆರೆನ ಇಲ್ಲ್ ಲೆನ ಲಕ್ಷಣೊಲು.<ref name="vlasenko"/> ಒಟ್ಟುಗು, ಕಟ್ಟಡೊದ ವಿಸ್ತೀರ್ಣ {{convert|3309.5|m2|sqft|2|abbr=on}} ಉಂಡು.<ref name="vr-829-р"/> ಗುಡ್ಡೆದ ಆಳೊಡು ಇಪ್ಪುನ ಕಟ್ಟಡೊದ ಕೆಳಮಟ್ಟೊಡು ರಡ್ಡ್ ಸ್ಟೆಬಲ್ಲು, ಕೋಚ್ ಮ್ಯಾನ್ಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಪಾಲ್ ಪಡೆಯಿನ ಬಟ್ಟೆ ಒಯ್ಪುನ ಜಾಗೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಪ್ರತ್ಯೇಕ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್. ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಸ್ಟೋರೇಜ್ ರೂಮ್ ಇತ್ತ್ಂಡ್. ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಸುರುತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ರಡ್ಡ್ ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್, ಬೊಕ್ಕ ರಡ್ಡನೆಡ್ ಮೂಜಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್.{{refn|೧೯೦೩ಡ್ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ ೫೪೦ ಬೊಕ್ಕ ೪೨೦ ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್, ಅಯಿಕ್ ಅನುಗುಣವಾದ್.{{sfn|Malakov|1999}}|group=nb}} ಕೆಳಮಟ್ಟೊದ ಮಿತ್ತ್ ದ ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿನ್ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಮಾತ್ರ ದೀಯೆರೆ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ರಡ್ಡನೆ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಫೋಯರ್, ಅಡುಗೆಮನೆ, ಬಫೆಟ್, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ಆರು ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಒಂಜೇ ಮಟ್ಟೊಡು ನಾಲ್ ವೈನ್ ಸೆಲ್ಲರ್ ಲಾ ಇತ್ತ್ಂಡ್.{{refn|Tಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಬೆಲೆ 1903ಡ್ 1,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ನೆಲಮಾಳಿಗೆಲು ಮಿತ್ತ್ ದ ಮಟ್ಟೊಡು ಉಪ್ಪುನ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲೆಗ್ ಸೇರ್ದಿನವು. ಮೂಜಿನೆ ಮಹಡಿಡ್, ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಎಂಟು ವಸತಿ ಕೊಠಡಿಲು, ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಸ್ಕಲ್ಲರಿ (ಕೋಣೆ)/ಪಾತ್ರೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಸೇವಕೆರೆಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು ಇತ್ತ್ಂಡ್.{{refn|ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸುರುತ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ 2,000 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಬಂಕೋವಾ ಸ್ಟ್ರೀಟ್ ದ ಮಟ್ಟೊಡ್ದ್ ಸ್ವಲ್ಪ ಕೆಳಗ್ ದೀಯೆರ್, ಎದುರುದ ಪ್ರವೇಶ ದ್ವಾರೊಡ್ದ್. ಹೊರೊಡೆಕಿಗ್ ಸೇರ್ದಿನ ಮಲ್ಲ ಅಪಾರ್ಟ್ಮೆಂಟ್, ಒಂಜಿ ಅಧ್ಯಯನ (ಕೋಣೆ), ಒಂಜಿ ಮಲ್ಲ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಲಿವಿಂಗ್ ರೂಮ್, ಒಂಜಿ ಡೈನಿಂಗ್ ರೂಮ್, ಒಂಜಿ ಬೌಡೋಯಿರ್, ಒಂಜಿ ಬೆಡ್ ರೂಮ್, ಒಂಜಿ ಜೋಕುಲೆನ ಕೋಣೆ, ಒಂಜಿ ಗವರ್ನೆಸ್ ಗ್ ಒಂಜಿ ಕೋಣೆ, ಒಂಜಿ ಅತಿಥಿ ಕೋಣೆ, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೋಣೆ, ಅಡುಗೆಮನೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಒಂಜಿ ಕೋಣೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲು.<ref name="Horodetskyi"/> ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಹೊರೊಡೆಕಿನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸಮಾನವಾದ್ ಗಾತ್ರ ಬೊಕ್ಕ ವಿನ್ಯಾಸೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಇತ್ತ್ಂಡ್.{{refn|ಈ ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆನ್ 1903ಡ್ ವಾರ್ಷಿಕ 3,500 ರೂಬಲ್ಸ್ ಗ್ ಬಾಡಿಗೆಗ್ ಕೊರ್ತೆರ್.{{sfn|Malakov|1999}}|group=nb}} ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಕೋಣೆ ಕಮ್ಮಿ ಇತ್ತ್ಂಡ್; ಇಂದೆನ್ ಸರಿ ಮಲ್ಪರೆ, ನಗರೊದ ವಿಹಂಗಮ ದೃಶ್ಯೊನು ಒದಗಾವುನ ಒಂಜಿ ಸಂಪರ್ಕ ಟೆರೇಸ್ ಇತ್ತ್ಂಡ್.<ref name="Horodetskyi"/>{{refn|The rental cost for this dwelling was 2,750 rubles annually. For comparison, an average salary for a librarian was about 50 rubles per year.{{sfn|Ivanenko|2004}}|group=nb}} ==ದಂತಕಥೆಲು== [[File:House with Chimaeras gardens.JPG|thumb|ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಉದ್ಯಾನೊಲು ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡ (ಕೀವ್) ಹಿನ್ನೆಲೆಡ್ ತೋಜುಂಡು]] ವರ್ಷೊಡ್ದಿಂಚಿ, ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಪ್ಪುನ ಇಲ್ಲ್ ದ ಅಸಾಮಾನ್ಯ ಸ್ವಭಾವೊ ಮಾರ್ಗದರ್ಶಿ-ಪುಸ್ತಕೊಲೆಡ್ ಅತ್ತಂದೆ ಪತ್ರಿಕೆಲೆಡ್ ಕೆಲವೊಂಜಿ ಸರ್ತಿ ಪುನರಾವರ್ತಿತವಾದ್ ಬರ್ಪಿನ ಒಂಜಿ ಸಂಖ್ಯೆದ ಕತೆಕುಲೆಗ್ ಕಾರಣವಾತ್ಂಡ್, ಆಂಡಲಾ ಅವು ಅಸತ್ಯ ಅತ್ತಂದೆ ಯಾವುದೇ ಪರಿಶೀಲಿಸಬಹುದಾದ ಮೂಲೊನು ಕಳೆವೊಂದಿಜ್ಜಿ.<ref>{{harvnb|Savchuk|1996|p=95.}}</ref> ಸುರುತ ದಂತಕಥೆದ ಪ್ರಕಾರ, ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಮಗೆ ಕೆಲವು ದುರದೃಷ್ಟಕರ ಪ್ರೇಮ ಸಂಬಂಧೊಡ್ದ್ ಅತ್ತಂಡ ಕುಟುಂಬದ ಜಗಳೊಡ್ದ್ ಡ್ನಿಪರ್ ನದಿಗ್ ಜಂಪ್ ಮಲ್ತ್ ದ್ ಆತ್ಮಹತ್ಯೆ ಮಲ್ತೆರ್.{{refn|It was actually previous owner's ([[Friedrich Mering|Professor Mering]]) daughter who drowned.{{sfn|Ivanenko|2004}}|group=nb}}ಫಲವಾದ್, ಹೊರೊಡೆಕಿ ಸ್ವಲ್ಪ ಪಿಚ್ಚರ್ ಆದ್ ತನ್ನ ಮಗೆನ ನೆಂಪುಗು ಈ ಕತ್ತಲೆದ ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="kievinfo">{{cite web|url=http://www.kiev.info/culture/house_chimeras.htm|title=House with Chimaeras|access-date=2006-02-16|first=Viktor|last=Pavlovsky |work=Kiev Info|publisher=Optima Tours}}</ref> ರಡ್ಡನೆ ದಂತಕಥೆ ಪಂಡ ಹೊರೊಡೆಕಿ ಕೆಲವು ಬೇತೆ ವಾಸ್ತುಶಿಲ್ಪಿಲೆನೊಟ್ಟುಗು ಒಂಜಿ ಪಂತೊನು ಮಲ್ತೆರ್, ವಾಸ್ತುಶಿಲ್ಪಿ ಅಲೆಕ್ಸಾಂಡರ್ ಸ್ಕೋಬೆಲೆವ್ ಸೇರ್ದ್, ಅಂಚನೆ ಭೂಪ್ರದೇಶೊಡು ಒಂಜಿ ಇಲ್ಲ್ ಕಟ್ಟೆರೆ ಅಸಾಧ್ಯ ಪನ್ಪಿನೆನ್ ಸಾಬೀತು ಮಲ್ಪೆರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್, ದಾಯೆ ಪಂಡ ಆ ಜಾಗೆ (ಇವಾನ್ ಫ್ರಾಂಕೊ ಥಿಯೇಟರ್ ದ ಸಮೀಪೊಡು) ಒಂಜಿ ಜವುಗು (''ಕೊಜ್'ಯೆ ಬೊಲೊಟೊ'') ಮಿತ್ತ್ ಉಂಡು. ಕೀವ್ ದ ನಿರ್ಮಾಣ ಸಮಿತಿ ಈ ನಿರ್ದಿಷ್ಟ ಲಾಟ್ ಡ್ ಏರ್ಲಾ ರಚನೆಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ನಿಷೇಧ ಮಲ್ತ್ಂಡ್, ಆಂಡ ಅಂಚನೆ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಡು ಹೊರೊಡೆಕಿ ಪಂತೊನು ಗೆಂದಿಯೆರೆ ಅನುವು ಮಲ್ತೆರ್.<ref name="kievinfo"/> ಮೂಜಿನೆ ದಂತಕಥೆದ ಪ್ರಕಾರ, ಹೊರೊಡೆಕಿ 1913ಡ್ ಶಾಪ ಕೊರ್ತೆರ್ (ಅರೆನ ಫೋರ್ಕ್ಲೋಸರ್/ಅರೆನ ಸಾಲಗಾರೆರೆಗ್ ಮರುಪಾವತಿ ಮಲ್ಪೆರೆ ಅಸಮರ್ಥತೆ ಕಾರಣ); ಇಲ್ಲ್ ದ ಮಾತಾ ಬಾಡಿಗೆದಾರೆರ್ ದುಖಿಡ್ ಉಪ್ಪುವೆರ್ ಅತ್ತ್ಂಡ ಒಂಜಿ ರೀತಿದ ಆರ್ಥಿಕ ದುರದೃಷ್ಟೊನು ಎದುರಿಸವೆರ್. ಕಟ್ಟಡೊದ ಒಂಜಿ ಭಾಗೊನು ಬಾಡಿಗೆಗ್ ದೆತೊನುನ ಮಾತಾ ವ್ಯವಹಾರೊಲು ಒಂಜಿ ವೇಳೆ ದಿವಾಳಿ ಪೋಯೆರ್, ಅಕ್ಲೆನ ನಿಧಿನ್ ಕಳವು ಮಲ್ತೆರ್ ಅತ್ತ್ಂಡ ರದ್ದು ಮಲ್ತೆರ್ ಪನ್ಪಿನ ಒಂಜಿ ಕತೆ ಉಂಡು.<ref name="mirtu"/> == ಉಲ್ಲೇಕೊಲು == === ಟಿಪ್ಪಣಿಲು === {{Reflist|group="nb"|30em}} === ಅಡಿಟಿಪ್ಪಣಿಲು === {{Reflist|30em}} === ಗ್ರಂಥಸೂಚಿ === {{Refbegin}} * {{Cite journal|last=Ivanenko|first=Anna|title=Horodecki. Lord of Chimaeras|url=http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|volume=1–2|journal=Mir Turizma|year=2004|location=[[Kyiv]]|issn=1811-3583|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20070506082208/http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|archive-date=2007-05-06}} * {{Cite book|last=Malakov|first=Dmytro|title=Arkhitektor Horodetskyi|publisher=Kyi|year=1999|location=[[Kyiv]]|isbn=966-7161-30-7|language=uk}} * {{Cite book|last=Malikenaite|first=Ruta|title=Touring Kyiv|publisher=Baltija Dryk|year=2003|isbn=966-96041-3-3|language=uk}} * {{Cite book|last=Savchuk|first=Galina|title=The Streets of Kiev: Five Walks in the Centre|publisher=ArtEc Publishers|year=1996|location=[[Kyiv]]}} * {{Cite book|last=Yasiievych|first=V. E.|title=Architecture of Ukraine at the Turn of the 19th–20th Centuries|publisher=Budivel'nyk|year=1988|location=[[Kyiv]]|isbn=5-7705-0088-3|oclc=19693625|language=ru}} * {{Cite book|last=Zharikov|first=N. L.|title=Monuments of Urban Development and Architecture in the Ukrainian SSR|chapter=Residential house (Horodecki's), 1904|chapter-url=http://ua.vlasenko.net/_pgs/pgs-html/pgs1-038.html|volume=1–4|publisher=Budivel'nyk|year=1983–1986|location=[[Kyiv]]|language=ru|lccn=84179019}} {{Refend}} ==ಬಾಹ್ಯ ಕೊಂಡಿಲು== {{Commons category|House with Chimeras (Kyiv)}} * {{Cite web|url=http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|title=Art Nouveau in Kyiv|first=Dmytro|last=Horbachov|access-date=2006-09-30|archive-url=https://web.archive.org/web/20041104061652/http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|archive-date=2004-11-04|url-status=dead|ref=none}} * {{Cite encyclopedia|url=http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|title=House with Chimaeras|encyclopedia=Wiki-Encyclopedia Kyiv|language=uk|access-date=2011-06-21|archive-date=2022-03-07|archive-url=https://web.archive.org/web/20220307105847/http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|url-status=dead}} * {{Cite web|url=http://www.interesniy.kiev.ua/old/architecture/buildings/2 |script-title=ru:Замки Киева: Легенды и были Замка Ричарда, Дом с Химерами, замок барона Штейнгеля|trans-title=Castles of Kiev: Legends were Richard's Castle, the House of Chimeras, Baron Steingel's castle |work=Interesniy Kiyev |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20061104033503/http://www.interesniy.kiev.ua/old/architecture/buildings/2 |archive-date=2006-11-04 }} * {{Cite web|url=http://brmr.livejournal.com/628752.html |title=Photogallery containing detailed images |publisher=[[LiveJournal]] |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20130814171250/http://brmr.livejournal.com/628752.html |archive-date=2013-08-14 }} * {{Cite web|url=http://www.domparketa.com.ua/ru/gallery/dom_ximer/ |title=Interior photogallery |work=domparketa.com.ua |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20060829142840/http://domparketa.com.ua/ru/gallery/dom_ximer/ |archive-date=2006-08-29 }} 39pt1p66u9zdbgfiwfbfq9zjgy9sdug 366204 366203 2026-08-21T10:35:26Z Hariprasad Shetty10 6127 366204 wikitext text/x-wiki {{Short description|ಕೀವ್, ಉಕ್ರೇನ್‌ಡ್ ಇಪ್ಪುನ ಆರ್ಟ್ ನುವೊ ವಾಸ್ತುಶಿಲ್ಪೊದ ಕಟ್ಟಡ}} {{Infobox building | name = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್ | native_name = Будинок з химерами | native_name_lang = uk | image = Будинок із химерами-3.JPG | image_alt = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ದೃಶ್ಯ | image_caption = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ಮುಖಭಾಗ | architectural_style = ಆರ್ಟ್ ನುವೊ | structural_system = ಕಾಂಕ್ರೀಟ್ ಪೈಲುಲು; ನಿರಂತರ ಅಡಿಪಾಯ | location = ಲಿಪ್ಕಿ, ಕೀವ್, ಉಕ್ರೇನ್ | address = 10, ಬ್ಯಾಂಕೋವಾ ಬೀದಿ | coordinates = {{coord|50|26|42|N|30|31|43|E|type:landmark_region:UA-30|display=inline,title}} | construction_start_date = 1901 | completion_date = 1902 | floor_count = 3 (ಬ್ಯಾಂಕೋವಾ ಬೀದಿ ಬದಿಟ್)<br>6 (ಫ್ರಾಂಕೊ ಚೌಕ ಬದಿಟ್) | floor_area = {{convert|3309.5|m2|sqft|2|abbr=on}} | architect = ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರಡೆಟ್ಸ್ಕಿ }} '''ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್''' ಅತ್ತಂದೆ '''ಹೊರೊಡೆಟ್ಸ್ಕಿ ಹೌಸ್''' (ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ಪುದರ್ ಡ್ ಪುದರ್ ದೀತಿನ) ಆರ್ಟ್ ನೌವಿಯೊ ಕಟ್ಟಡ ಉಂದು. ಲಿಪ್ಕಿ ಕಿವ್ ದ ನೆರೆಹೊರೆ, ಉಕ್ರೇನ್ ದೇಶದ ರಾಜಧಾನಿ. ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆನ ಕಚೇರಿದ ಬೀದಿದ ಎದುರುಡು ಉಪ್ಪುನ ಈ ಕಟ್ಟಡೊನು 2005 ಡ್ದ್ ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಅಧ್ಯಕ್ಷೆರೆನ ನಿವಾಸೊ ಆದ್ ಬಳಕೆ ಮಲ್ತೊಂದು ಬೈದೆರ್.<ref name="vr-340">{{Cite Ukrainian law|type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=340|law=340-2005-п|name=On the addition to the house on 10, Bankova St. in the city of Kyiv the status of a government residence|date=2005-05-11}}</ref><ref name="BBC Ukrainian">{{cite news|url=https://www.bbc.com/ukrainian/indepth/story/2005/04/050401_ukraine_residences |title=The aura is fine, but the place is too small|access-date=2006-09-30|first=Marta|last=Shokalo|date=2005-04-01|publisher=[[BBC News|BBC Ukrainian]]|language=uk}}</ref> == ಸ್ಥಳ ಬೊಕ್ಕ ಇತ್ತೆದ ಬಳಕೆ == ಕಟ್ಟಡೊದ ಎದುರುದ ಬೀದಿನ್ ಮಾತಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ಗ್ ಮುಚ್ಚಿದ್ ದೀತೆರ್, ಬೊಕ್ಕ ಇತ್ತೆ ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡಗ್ ಸಮೀಪೊಡು ಉಪ್ಪುನೆರ್ದಾತ್ರ ಉಂದು ಒಂಜಿ ಗಸ್ತು ಪಾದಚಾರಿ ವಲಯ ಆದುಂಡು. 2005 ಡ್ದ್ ಈ ಕಟ್ಟಡೊನು ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಅಧ್ಯಕ್ಷೆರೆನ ನಿವಾಸೊ ಆದ್ ಬಳಸೊಡು ಬೈದೆರ್.<ref name="vr-340"/><ref name="BBC Ukrainian"/> == ನಿರ್ಮಾಣ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಿ == ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ [[ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ]] ಮೂಲತಃ 1901–1902 ಡ್ ತನ್ನ ಸ್ವಂತ ಅಪ್ಮಾರ್ಕೆಟ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಕಟ್ಟಡೊ ಆದ್ ಬಳಕೆ ಮಲ್ಪುನ ನಿಟ್ಟಿಡ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಹೊರೊಡೆಕಿ ಆರ್ಟ್ ನೌವಿಯೊ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರಭಾವೊನು ಕಟ್ಟಡೊದ ವಿನ್ಯಾಸೊಡು ಬಳಸಿನೆರ್. ಕಟ್ಟಡೊದ ಹೊರಭಾಗೊಡು ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲು, ಪೌರಾಣಿಕ ಜೀವಿಲು ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲೆನ ಅಲಂಕಾರೊಲು ವಿಶೇಷ ಆಕರ್ಷಣೆ ಆದುಂಡು. == ಮಾಲೀಕತ್ವೊದ ಬದಲಾವಣೆಲು == ವರ್ಷೊಲು ಕರಿಲೆಕನೆ, ಹೊರೊಡೆಕಿ ಕಡೆಕ್ ಆರ್ಥಿಕ ತೊಂದರೆಲೆಡ್ದ್ ಕಟ್ಟಡೊನು ಮಾರಾಟ ಮಲ್ಪೊಡಾಂಡ್. ಅಯಿಡ್ದ್ ಬೊಕ್ಕ ಕಟ್ಟಡೊ ಅನೇಕ ಸರ್ತಿ ಮಾಲೀಕತ್ವೊನು ಬದಲಾವಣೆ ಮಲ್ತ್ಂಡ್. 2000 ದ ಸುರುತ ಮುಟ್ಟ ಉಕ್ರೇನ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಅಧಿಕೃತ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಕ್ರಮಣ ಮಲ್ಪುನ ದುಂಬು ಕಟ್ಟಡೊ ಬೇತೆ ಬೇತೆ ಬಳಕೆಲೆಗ್ ಒಳಪಟ್ಟಿತ್ತ್ಂಡ್.<ref name="riddle"/> == ಪುನಃಸ್ಥಾಪನೆ == ಕಟ್ಟಡೊನು ಖಾಲಿ ಮಲ್ತಿನಗ, ಅಯಿತ ಒಳಾಂಗಣ ಬೊಕ್ಕ ಪಿದಯಿದ ಅಲಂಕಾರೊನು ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆನ್ ಅನುಸರಿಸಾದ್ ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆ ಮಲ್ತೆರ್.<ref name="mirtu">{{harvnb|Ivanenko|2004}}</ref> ಪುನಃಸ್ಥಾಪನೆದ ಪಿರವುಟು ಕಟ್ಟಡೊದ ಮೂಲ ವಾಸ್ತುಶಿಲ್ಪೀಯ ರೂಪ ಬೊಕ್ಕ ಅಲಂಕಾರೊನು ಸಾಧ್ಯವಾದಷ್ಟು ಉಳಿಸಾದ್ ದೀತೆರ್. ಇಂದ್ ಉಂದು ಕೀವ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಕಟ್ಟಡೊಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. == ಚಿಮೇರಾಸ್ ಪುದರ್ ಬತ್ತಿನ ಕಾರಣ == ಹೊರೊಡೆಕಿ ಒರಿ ಉತ್ಸಾಹಿ ಬೇಟೆಗಾರೆ ಆಯಿನೆಡ್ದಾವರ, ಇಟಾಲಿಯನ್ ವಾಸ್ತುಶಿಲ್ಪಿ ಬೊಕ್ಕ ಶಿಲ್ಪಿ '''ಎಮಿಲಿಯೊ ಸಾಲಾ''' ಕಟ್ಟಡೊದ ಅಲಂಕಾರೊಲೆಗ್ ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲೆನ್ ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲೆನ್ ಬಳಸಿನೆರ್.<ref name="vinnytsia_library"/> ಈ ವಿಶೇಷ ಅಲಂಕಾರೊಲೆಡ್ದ್ ಈ ಕಟ್ಟಡೊಗು '''ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್''' ಪುದರ್ ಬತ್ತ್ಂಡ್. == ಚಿಮೇರಾಸ್ ಅತ್ತಂದೆನ ಅರ್ಥ == ಈ ಪುದರ್ ಪುರಾಣೊದ [[ಚಿಮೇರಾ]] ಜೀವಿನ್ ನೇರವಾದ್ ಸೂಚಿಸವುಜಿ. ಬದಲಾತ್, ಕಟ್ಟಡೊದ ವಾಸ್ತುಶಿಲ್ಪೊಡು ಪ್ರಾಣಿಲು ಬೊಕ್ಕ ಪೌರಾಣಿಕ ಜೀವಿಲೆನ ವಿಚಿತ್ರ ರೂಪೊಲೆನ್ ಅಲಂಕಾರಿಕ ಅಂಶೊಲೆನ್ ಆದ್ ಬಳಸುನ ಶೈಲಿನ್ ಸೂಚಿಸುಂಡು. ಕಟ್ಟಡೊದ ಗೋಡೆಲು, ಮೇಲ್ಚಾವಣಿ ಬೊಕ್ಕ ಮುಖಭಾಗೊಡು ವಿವಿಧ ಪ್ರಾಣಿಲೆನ ಶಿಲ್ಪೊಲು ಇಪ್ಪುನ ಕಾರಣೊಡು ಉಂದು ಕೀವ್‌ದ ವಿಶಿಷ್ಟ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಕಟ್ಟಡೊಲೆಡ್ ಒಂಜಿ ಆತ್ ಪರಿಗಣಿಸುಂಡು. == ಎಮಿಲಿಯೊ ಸಾಲಾ ಬೊಕ್ಕ ಶಿಲ್ಪಕಲೆ == ಇಟಾಲಿಯನ್ ವಾಸ್ತುಶಿಲ್ಪಿ ಬೊಕ್ಕ ಶಿಲ್ಪಿ '''ಎಮಿಲಿಯೊ ಸಾಲಾ''' ಕಟ್ಟಡೊದ ಅಲಂಕಾರಿಕ ಶಿಲ್ಪೊಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರೆ ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಶಿಲ್ಪೊಲೆಡ್ ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲು, ಸಮುದ್ರ ಜೀವಿಲು ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲು ಮುಖ್ಯ ಆಂಡ್.<ref name="vinnytsia_library"/> ಈ ಶಿಲ್ಪೊಲು ಕಟ್ಟಡೊದ ಆರ್ಟ್ ನೌವಿಯೊ ವಿನ್ಯಾಸೊಗು ಒಂಜಿ ವಿಶಿಷ್ಟ ರೂಪೊನು ಕೊರಿಯೆರ್. == ವಾಸ್ತುಶಿಲ್ಪೀಯ ಪ್ರಭಾವೊ == ಹೊರೊಡೆಕಿನ ಅನನ್ಯ ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿ ಕೀವ್‌ಡು ಮಾತ್ರ ಅಲ್ಲದೆ ಅಂತರರಾಷ್ಟ್ರೀಯ ಮಟ್ಟೊಡುಲಾ ಗಮನ ಸೆಳೆದ್ಂಡ್. ಅರೆನ ಕಟ್ಟಡೊದ ವಿನ್ಯಾಸೊನು ಸ್ಪೇನ್‌ದ ಪ್ರಸಿದ್ಧ ವಾಸ್ತುಶಿಲ್ಪಿ [[ಆಂಟೋನಿ ಗೌಡಿ]]ದ ಶೈಲಿದೊಟ್ಟುಗು ಹೋಲಿಸಾಯಿನವುಲು ಉಂಡು.<ref name="mirtu">{{harvnb|Ivanenko|2004}}</ref><ref name="element">{{cite news|url=http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |title=heed the krai |access-date=2006-09-30 |first=Sonya |last=Rinkus |work=elementmoscow.ru |url-status=dead |archive-url=https://web.archive.org/web/20071109061542/http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |archive-date=2007-11-09 }}</ref> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವೊ == ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್ ಕೀವ್‌ದ ಆರ್ಟ್ ನೌವಿಯೊ ವಾಸ್ತುಶಿಲ್ಪದ ಒಂಜಿ ಪ್ರಮುಖ ಉದಾಹರಣೆ ಆಂಡ್. ಕಟ್ಟಡೊದ ವಿಶಿಷ್ಟ ಶಿಲ್ಪೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರೊಲು ಉಕ್ರೇನ್‌ದ ರಾಜಧಾನಿದ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಗುರುತಿನ ಒಂಜಿ ಭಾಗ ಆದುಂಡು. ==ಇತಿಹಾಸ== ===ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಸುರುತ ಇತಿಹಾಸ=== ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಒಂಜಿ ಇಲ್ಲ್ ನ್ ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ೧೯೦೧-೧೯೦೨ಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್.<ref name="mirtu"/> ಹೊರೊಡೆಕಿ ೧೮೬೩ಡ್ ಪೊಡೊಲಿಯಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಸಮೃದ್ಧ ಪೋಲಿಷ್ ಜನಕುಲು ಸ್ಜ್ಲಾಚ್ಟಾ (ಉತ್ತಮ) ಕುಟುಂಬೊಡು ಪುಟ್ಟುವೆರ್.<ref name="vinnytsia_library">{{cite web|url=http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |title=Architector of century |date=2003-05-20 |access-date=2007-07-14 |publisher=Vinnytsia oblast' universal science library named after K.A.Timiryazev |language=uk |archive-url=https://web.archive.org/web/20070928062123/http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |archive-date=September 28, 2007 |url-status=dead }}</ref> ೧೮೯೦ಡ್ ಇಂಪೀರಿಯಲ್ ಅಕಾಡೆಮಿ ಆಫ್ ಆರ್ಟ್ಸ್ ನ್ ಸೈಂಟ್ ಪೀಟರ್ಸ್ಬರ್ಗ್ ಮುಗಿತ್ ಬೊಕ್ಕ, ಆರ್ ಕೀವ್ ಗ್ ಪೋಯೆರ್, ಅಲ್ಪ ಆರ್ ಸುಮಾರ್ ೩೦ ವರ್ಸೊಗು ಜೀವನ ಮಲ್ತೆರ್.<ref name="vinnytsia_library"/> ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಹೊರೊಡೆಕಿ ಒರಿ ಪ್ರಮುಖ ಕೀವ್ ವಾಸ್ತುಶಿಲ್ಪಿಯಾದ್ ತನ್ನನ್ ತಾನ್ ಸ್ಥಾಪಿಸೊಂದಿತ್ತೆರ್, ಅರೆನ ನಿಕಟ ಸ್ನೇಹಿತೆ ಬೊಕ್ಕ ಪಾಲುದಾರ ಎಂಜಿನಿಯರ್ ಆಂಟನ್ ಸ್ಟ್ರಾಸ್ ನೊಟ್ಟುಗು ಮಸ್ತ್ ನಗರ ಕಟ್ಟಡೊಲೆನ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್, ಸೆಂಟ್. ನಿಕೋಲಸ್ ರೋಮನ್ ಕ್ಯಾಥೊಲಿಕ್ ಕ್ಯಾಥೆಡ್ರಲ್, ಕೀವ್ ಡ್ದ್ ಕರೈಟೆ ಕೆನೆಸಾ (ಕೀವ್) ಬೊಕ್ಕ ಇನಿ ಉಕ್ರೇನ್ ದ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ಮ್ಯೂಸಿಯಂ. ವಾಸ್ತುಶಿಲ್ಪೊದ ಜೊತೆಗೆ, ಹೊರೊಡೆಕಿಗ್ ಮಲ್ಲ-ಆಟದ ಬೇಟೆ ಡ್ ಆಸಕ್ತಿ ಇತ್ತ್ಂಡ್,<ref name="vinnytsia_library"/> ಉಂದು ಅರೆನ ಕಟ್ಟಡೊಡು ದಾಯೆಗ್ ಮಸ್ತ್ ಪ್ರಾಣಿಲೆನ್ ತೋಜಾವುಂಡು ಪನ್ಪಿನೆನ್ ವಿವರಿಸವುಂಡು. [[File:House with Chimaeras during construction.jpg|thumb|left|ನಿರ್ಮಾಣೊಡು ಉಪ್ಪುನ ಕಟ್ಟಡ, ೧೯೦೨]] ಹೊರೊಡೆಕಿ ಸಾಲದ ದುಡ್ಡುಡ್ದ್ ಇಲ್ಲ್ ದ ನಿರ್ಮಾಣೊಗು ಹಣಕಾಸು ಮಲ್ತೆರ್,<ref name="mirtu"/><ref name="KV"/> ಉಂದು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಆಯೆರೆ ಉದ್ದೇಶೊಡು.<ref name="Horodetskyi">{{harvnb|Malakov|1999|p=?}}</ref><ref name="Touring Kyiv">{{harvnb|Malikenaite|2003|p=50.}}</ref> ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಲಾ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ರೂಪಿಸೊಂದಿತ್ತ್ಂಡ್, ಉಂದೆನ್ ಎಲಿವೇಟರ್] ಬೊಕ್ಕ ಮೆಟ್ಟಿಲುಲೆಡ್ದ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್.{{refn|ಉಂದೆಕ್ ಒಂಜೇ ಅಪವಾದ ಪಂಡ ಕಟ್ಟಡೊದ ಕೆಳಮಹಡಿಡ್ ರಡ್ಡ್ ಬೇತೆ ಬೇತೆ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್.|group=nb}} ಹೊರೊಡೆಕಿ ಸ್ವತಃ ಕಟ್ಟಡದ ನಾಲ್ನೇ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ತೆರ್, ಸುಮಾರ್{{convert|380|m2|sqft|abbr=on}}.<ref name="Horodetskyi"/>{{refn|ಅರೆನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಸೂಕ್ತವಾಯಿನವು. ನಂ. 3, ಮುಖ್ಯ ಮಹಡಿ, ಬಂಕೋವಾ ಬೀದಿದ ಮಟ್ಟೊಡ್ದ್ ತೂಂಡ.|group=nb}} ಹೊರೊಡೆಕಿ ಫೆಬ್ರವರಿ ೧, ೧೯೦೧ಡ್ ಸುರುತ ಜಮಿನ್ ನ್ ಕೊಂಡೊಂತೆರ್, ಆ ವರ್ಷದ ಮಾರ್ಚ್ ೧೮ಡ್ ನಿರ್ಮಾಣದ ಬೇಲೆ ಸುರು ಆಂಡ್. ಪಿದಯಿದ ಗೋಡೆಲೆನ ನಿರ್ಮಾಣ ಆಗಸ್ಟ್ ೨೧ ಗ್ ಮುಗಿಂಡ್, ಬೊಕ್ಕ ಛಾವಣಿನ್ ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಮಾತಾ ಕಲ್ಲುದ ಬೇಲೆನ್ ಸೆಪ್ಟೆಂಬರ್ ೧೩ ಗ್ ಮುಗಿತ್ಂಡ್.<ref name="Horodetskyi"/><ref name="Touring Kyiv"/> ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ದ ಒಳಗೆ ಆರ್ಥಿಕ ಕಷ್ಟೊಲೆಡ್ದ್, ಕಟ್ಟಡೊದ ಪೂರ್ಣಗೊಳಿಸುವಿಕೆ ತಡವಾಂಡ್. ಮೇ ೧೯೦೩ಡ್, ಕೆಳಮಟ್ಟೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಬೊಕ್ಕ ಹೊರೊಡೆಕಿನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಮಾತ್ರ ಆಕ್ರಮಣ ಮಲ್ತೆರ್.<ref name="Horodetskyi"/> ಭೂಮಿ ಬೊಕ್ಕ ನಿರ್ಮಾಣದ ಒಟ್ಟು ವೆಚ್ಚ 133,000 ಆಂಡ್ ರಷ್ಯನ್ ರೂಬಲ್.{{refn|ಈ ಬೆಲೆ ೧೯೦೩ ಡ್ದ್ ಉಂಡು.[https://web.archive.org/web/20170323182358/http://www.archives.gov.ua/Archives/index.php?ra27 Government Archive of the City of Kyiv] – [[Fonds|Fond]] No. 143, Series 2, File No. 520, Item No. 9. In the late 19th and early 20th century, one thousand [[Russian ruble]]s were worth about 24.89 [[gold]] [[troy ounce]]s, or roughly one million Russian rubles as of 2011.{{sfn|Malakov|1999}}|group=nb}} ಒಟ್ಟುಗು, {{convert|1550|m2|sqft|abbr=on}} ಜಮಿನ್ ನ್ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಗು ಬಳಕೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಒಟ್ಟು 15,640 ರೂಬಲ್ಸ್ ಖರ್ಚು ಆಂಡ್.<ref name="KV">{{cite web|url=http://old.kv.com.ua/index.php?rub=46&number_old=2984|title=The rebirth of the House with Chimaeras|access-date=2006-07-14|first=Mikhail|last=Kalnitsky|work=Kievskie Vedomosti №179 (2984)|language=ru|date=2003-08-19|archive-date=2018-08-20|archive-url=https://web.archive.org/web/20180820045803/http://old.kv.com.ua/index.php?rub=46&number_old=2984|url-status=dead}}</ref> ಬಾಡಿಗೆಡ್ದ್ ವಾರ್ಷಿಕ ಲಾಭ 7,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್. ಹೊರೊಡೆಕಿ ತಾಜಾ ಇನ್-ಹೌಸ್ ಹಾಲುದ ಒತ್ತಾಯೊಗು ಕಾರಣವಾದ್ ಒಂಜಿ ಗೋಕುಲೊನು ಆವರಣೊಡು ದೀಯೆರ್,<ref name="mirtu"/><ref name="Touring Kyiv"/> ಗಾಯೊಲೆನ ವಾಸನೆ ಬಾಡಿಗೆದಾರೆರೆಗ್ ತೊಂದರೆ ಆವಂದಿಲೆಕ ನಿರ್ದಿಷ್ಟವಾದ್ ದೀದ್ ಇತ್ತ್ಂಡಲಾ. ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಒಂಜಿ ಜಾಗೆಡ್, ಒಂಜಿ ಎಲ್ಯ ಆಲ್ಪೈನ್ ಗಾರ್ಡನ್ (ಅಂದಾಜಿ. {{convert|320|m2|sqft|-2|abbr=on|disp=or}}) ಬೊಕ್ಕ ಒಂಜಿ ಫೌಂಟೇನ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರೆನ ಸಫಾರಿ ಬೇಟೆದ ಹವ್ಯಾಸೊನು ಒಳಗೊಂಡ ಆರ್ಥಿಕ ದುರ್ಬಳಕೆಡ್ದ್,<ref name="mirtu"/> ಜುಲೈ ೧೯೧೨ಡ್, ಹೊರೊಡೆಕಿ ಕೀವ್ ಮ್ಯೂಚುಯಲ್ ಕ್ರೆಡಿಟ್ ಅಸೋಸಿಯೇಷನ್ ​​ಡ್ದ್ ದೆತೊನುನ ಸಾಲೊಗು ವಿರುದ್ಧವಾದ್ ಕಟ್ಟಡೊನು ಜಾಮೀನು ಆದ್ ವಾಗ್ದಾನ ಮಲ್ತೆರ್.<ref name="riddle">{{cite web|title=Riddle of House with Chimaeras|url=http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|first=Yuliya|last=Ivashko|work=Vash Kiev|access-date=2006-07-14|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20120206003140/http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|archive-date=2012-02-06}}</ref> ಹೊರೊಡೆಕಿ ಸಾಲೊಡು ಡಿಫಾಲ್ಟ್ ಆನಗ, ಕಟ್ಟಡೊನು ೧೯೧೩ಡ್ ಹರಾಜು ಮಲ್ತೆರ್,<ref name="riddle"/> ಬೊಕ್ಕ ಎಂಜಿನಿಯರ್ ಡೇನಿಯಲ್ ಬಾಲಾಖೊವ್ಸ್ಕಿನ ಆಸ್ತಿ ಆಂಡ್,<ref name="mirtu"/> ಕೀವ್ ವ್ಯಾಪಾರಿನ ಮಗೆ,<ref name="KV"/> ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆದ ನಿರ್ದೇಶಕ ಮಂಡಳಿದ ಅಧ್ಯಕ್ಷೆರಾದಿತ್ತೆರ್,<ref name="KV"/> ಬೊಕ್ಕ ಕೀವ್ ಡ್ ಒರಿ ಫ್ರಾನ್ಸ್/ಫ್ರೆಂಚ್ ಕಾನ್ಸುಲಾರ್ ಏಜೆಂಟ್.<ref name="Fyodorovskaya">{{cite web|url=http://www.house-ua.com/news395.html |title=Hunter from the House with Chimaeras |access-date=2006-07-14 |first=Olga |last=Fyodorovskaya |work=Ukrainskiy Dom |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20070929175434/http://www.house-ua.com/news395.html |archive-date=September 29, 2007 }}</ref> ೧೯೧೬ಡ್, ಈ ಇಲ್ಲ್ ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆಗ್ ಸೇರ್ದಿನವು.<ref name="riddle"/> ೧೯೧೮ಡ್, ಕಟ್ಟಡೊದ ಮಾಲೀಕತ್ವ ಮತ್ತೆ ಬದಲಾಂಡ್, ಸ್ಯಾಮ್ಯುಯೆಲ್ ನೆಮೆಟ್ಸ್ ಗ್.<ref name="riddle"/> ೧೯೨೧ಡ್, ಬೊಲ್ಶೆವಿಕ್ ಲು ಕೀವ್ ನ್ ನಿಯಂತ್ರಣ ಮಲ್ತಿನ ಬುಕ್ಕೊ, ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್/ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್ ದ ಕೆಲವು ಇಲಾಖೆಲು ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಸದನೊಡು ಕಚೇರಿಲೆನ್ ದೆತೊನುವೆರ್. ===ಒಡೆತನ ೧೯೨೧–೨೦೦೨=== ರಷ್ಯಾದ ಕ್ರಾಂತಿದ ನಂತರದ ಉಕ್ರೇನ್/ಅಶಾಂತಿದ ಅವಧಿ ರಷ್ಯಾದ ಕ್ರಾಂತಿ (೧೯೧೭) ನಂತರ, ಕಟ್ಟಡೊನು ರಾಷ್ಟ್ರೀಕರಣ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನನೊಂಜಿ ಸರ್ತಿ ಕಮ್ಯುನಲ್ಕಾ/ಸಾಮುದಾಯಿಕ ಜೀವನಗ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="president.gov.ua">{{cite web|url=http://www.president.gov.ua/en/slideshow/3.html |title=House with Chimeras |access-date=2008-03-20 |work=[[President of Ukraine|Official web-site of President of Ukraine]] |url-status=dead |archive-url=https://web.archive.org/web/20080425002336/http://www.president.gov.ua/en/slideshow/3.html |archive-date=2008-04-25 }}</ref> ಪ್ರತಿಯೊಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಸುಮಾರ್ ಒಂಜಿ ರ್ದ್ ಪತ್ತ್ ಕುಟುಮೊಲು ನೆಲೆಯಾದಿತ್ತೆರ್.<ref name="Horodetskyi"/> ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧ (೧೯೪೧-೧೯೪೩) ದ ಪೊರ್ತುಡು, ಈ ಕಟ್ಟಡೊನು ಬುಡ್ದು ಪೋಯೆರ್. ಯುದ್ಧದ ಸಮಯೊಡು ಕಠಿಣ ಅಂಶೊಲೆಗ್ ಒಡ್ಡಿಕೊಳ್ಳುನ ಕಾರಣ, ಕಟ್ಟಡೊಗು ಅಯಿತ ರಚನೆಗ್ ಮಸ್ತ್ ಹಾನಿ ಆಂಡ್.<ref name="Horodetskyi"/> ಯುದ್ಧೊದ ನಂತರ, ಈ ಕಟ್ಟಡೊನು ಇವಾನ್ ಫ್ರಾಂಕೊ ರಾಷ್ಟ್ರೀಯ ಶೈಕ್ಷಣಿಕ ನಾಟಕ ರಂಗಮಂದಿರಡ್ದ್ ಸ್ಥಳಾಂತರಿಸಾಯಿನ ನಟೆರೆಗ್ ನಿವಾಸವಾದ್ ಸಂಕ್ಷಿಪ್ತವಾದ್ ಬಳಕೆ ಮಲ್ತೆರ್;<ref name="KV"/> ಆಂಡಲಾ, ಉಕ್ರೇನಿಯನ್ ಸೋವಿಯೆತ್ ಸಮಾಜವಾದಿ ಗಣರಾಜ್ಯೊದ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಫ್ ಉಕ್ರೇನ್ ದ ಕೇಂದ್ರ ಸಮಿತಿ ಈ ಕಟ್ಟಡೊದ ಒಡೆತನೊನು ದೆತೊನುಂಡು ಬೊಕ್ಕ ಅಯಿನ್ ಅಕ್ಲೆನ ಗಣ್ಯೆರೆಗ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ನಂಬರ್ ೧ ಆದ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="Horodetskyi"/> ಪಾಲಿಕ್ಲಿನಿಕ್ 20ನೆ ಶತಮಾನೊದ ಅಂತ್ಯೊಗು ಮುಟ್ಟ ಈ ಕಟ್ಟಡೊನು ಬಳಕೆ ಮಲ್ತ್ಂಡ್. ಆ ಸಮಯೊಡು, ಕಟ್ಟಡ ಬಹುತೇಕ ಅರ್ಧೊಗು ಪುಡಾಂಡ್. ಒಂಜಿ ಭಾಗ {{convert|22|cm|in|0|abbr=on}} ನ್ ಸಾಗ್ ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ ಒಂಜಿ ಪ್ರಮುಖ ಲಂಬ ಬಿರುಕು ರೂಪುಗೊಂಡುಂಡು, ಉಂದೆತ ಅಗಲ ಸುಮಾರ್ {{convert|40|cm|in|0|abbr=on}} ಉಂಡು.<ref name="Horodetskyi"/> ಕಟ್ಟಡೊದ ಕೆಲವು ವಾಸ್ತುಶಿಲ್ಪದ ವಿವರೊಲು ಒಂಜಿ ವೇಳೆ ಚಿಪ್ ಆತ್ಂಡ್, ಅತ್ತ್ಂಡ ಬಿರುಕು ಬುಲೆದ್ಂಡ್. ಕಟ್ಟಡೊದ ಜೀರ್ಣೋದ್ಧಾರದ ಬೇಲೆನ್ 2002 ಗ್ ನಿಗದಿ ಮಲ್ತೆರ್, ಆಂಡಲಾ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಆಪರೇಟರ್ ಲು 40 ವರ್ಸೊಡ್ದ್ ಜಾಸ್ತಿ ಕಟ್ಟಡೊನು ಆಕ್ರಮಣ ಮಲ್ತ್ ದ್ ಬುಡುದು ಪೋಯೆರೆ ಹಿಂಜರಿಯೊಂದಿತ್ತೆರ್. ಕಟ್ಟಡೊಡ್ದ್ ನಿವಾಸಿಲೆನ್ ಪಿದಯಿ ಕನರೆ, ಕೆಲಸದಕುಲು ಮಾತಾ ಕಿಟಕಿಲೆನ್ ಮುಚ್ಚಿಯೆರ್ ಬೊಕ್ಕ ಪಾಲಿಕ್ಲಿನಿಕ್ ಆವರಣೊನು ಖಾಲಿ ಮಲ್ತ್ಜಿಂಡ ಬಾಗಿಲುಲೆನ್ ಲಾ ಅಂಚನೆ ಮಲ್ಪುವೆ ಪಂದ್ ಬೆದರಿಕೆ ಮಲ್ತೆರ್. ಈ ವಿಷಯೊಡು ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ನ ಪಾಲ್ ಪಡೆಯಿನೆಡ್ದಾವರ ಮಾತ್ರ ಪಾಲಿಕ್ಲಿನಿಕ್ ನ್ ಸಂಪೂರ್ಣವಾದ್ ಪಿದಯಿ ಬುಡ್ಪಾಯೆರ್.<ref name="mirtu"/> ===ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಅಧಿಕೃತ ಬಳಕೆ=== [[File:Flags of EU and Ukraine.jpg|thumb|ಒಂಜಿ ಅಧಿಕೃತ ಕಾರ್ಯಕ್ರಮೊದ ಪೊರ್ತುಡು ಕಟ್ಟಡೊದ ಎದುರು ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪಿಯನ್ ಒಕ್ಕೂಟದ ಧ್ವಜೊಲು]] ಜೀರ್ಣೋದ್ಧಾರದ ಸಮಯೊಡು, ''ಪ್ರಾಜೆಕ್ಟ್ ರೆಸ್ಟಾವ್ರಾಟ್ಸಿಯಾ'' ನಡಪಾಯಿನ ಬೊಕ್ಕ ನಟಾಲಿಯಾ ಕೊಸೆಂಕೊ ಮುಖ್ಯಸ್ಥೆರಾದ್,<ref name="vr-829-р">{{Cite Ukrainian law |type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=829-р|law=829-2003-р |name=On the recognition of the project and the company carrying out the building's reconstruction as well as the restoration of the monument of architecture and the government monument of the city on Bankova St., 10 in the city of Kyiv|date=2003-12-31}}</ref> ಕಟ್ಟಡದ ಅಡಿಪಾಯೊನು ಬಲಪಡಾಯೆರೆ ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟ ಕಾಲೊಡು ತುಂಬಿನ ಇಡೀ ಕೆಳಮಹಡಿನ್ ಕೆಲಸದಕುಲು ಕಂಡು ಕನತೆರ್.<ref name="mirtu"/> ಒಳಾಂಗಣದ ವಿಸ್ತಾರವಾದ್ ಅಲಂಕಾರೊನು ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪೊಡಾಂಡ್. ಅಂಗಳೊಡು, ಪುನಃಸ್ಥಾಪಕೆರ್ ಒಂಜಿ ಕೃತಕ ಕೆರೆ, ಕಾರಂಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಎಲ್ಯ ಉದ್ಯಾನೊನು ದೀಯೆರ್—ಉಂದು ಮಾತ ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆಡ್ ಇತ್ತ್ಂಡ್.<ref name="mirtu"/> ಈ ಕಟ್ಟಡೊನು ನವೆಂಬರ್ ೨೦೦೪ಡ್ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ "ಉಕ್ರೇನಿಯನ್ ಕಲೆತ ಮೇರುಕೃತಿಲು" ಆದ್ ತೆರೆಯೆರ್.<ref name="museum">{{cite news|url=http://www.korespondent.net/main/31543 |title=A cultural museum center opens in the House with Chimaeras |access-date=2007-07-12 |date=2004-11-19 |work=[[Korrespondent]] |publisher=Bigmir-Internet |language=uk |archive-url=https://web.archive.org/web/20070927234156/http://www.korespondent.net/main/31543 |archive-date=September 27, 2007 |url-status=dead }}</ref> ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಆದ್ ಬೊಕ್ಕ ರಾಜ್ಯೊದ ಸಂದರ್ಶಕೆರೆಗ್ ಅಧ್ಯಕ್ಷೀಯ ಸಭೆತ ಜಾಗೆ ಆದ್ ದ್ವಿಗುಣ ಉದ್ದೇಶೊಗು ಸೇವೆ ಮಲ್ಪುಂಡು ಪನ್ಪಿನ ನಿರೀಕ್ಷೆ ಇತ್ತ್ಂಡ್. ಏಪ್ರಿಲ್ ೨೦೦೫ಡ್, ಕೀವ್ ಸಿಟಿ ಕೌನ್ಸಿಲ್ ಒಂಜಿ ಮಸೂದೆನ್ ಸಲ್ಲಿಸಯೆರ್<ref>[http://www.kmr.gov.ua/decree.asp Decrees of Kyiv City Council] {{webarchive|url=https://web.archive.org/web/20070707053720/http://kmr.gov.ua/decree.asp |date=2007-07-07 }} [[Kyiv City Council|Kyiv City Council decree]] No. '''380/2955''': [http://www.kmr.gov.ua/decree.asp?Id=2235 On the allowance of the change of ownership to the government authority with permission of conducting reconstruction and restoration works on the building of 10, Bankova Street] {{webarchive|url=https://web.archive.org/web/20070927225321/http://www.kmr.gov.ua/decree.asp?Id=2235 |date=2007-09-27 }}. Passed on 2005-04-21. {{in lang|uk}}</ref> [[ಉಕ್ರೇನಿಯನ್ ಹೈವ್ನಿಯಾ|₴]]104 ಮಿಲಿಯನ್ (ಅಂದಾಜಿ ಯುನೈಟೆಡ್ ಸ್ಟೇಟ್ಸ್ ಡಾಲರ್/ 20 ಮಿಲಿಯನ್) ಗ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರಗ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮನೆತ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆಗ್.<ref name="proUA">{{cite news|url=http://ua.proua.com/news/2005/05/11/180559.html|archive-url=https://web.archive.org/web/20070930044045/http://ua.proua.com/news/2005/05/11/180559.html|url-status=dead|archive-date=2007-09-30|title="House with Chimaeras" received the status of an official governmental residence|access-date=2006-09-30|date=2004-11-19|work=proUA|language=uk}}</ref> ಅಧಿಕೃತ ಸಮಾರಂಭೊಲೆಡ್ ಬಳಕೆ ಮಲ್ಪುನ ಕಟ್ಟಡೊದ ಎದುರುಡು ಒಂಜಿ ಪೊಸ ಚೌಕೊನು (ಎಲ್ಲಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ನ್ ಮುಚ್ಚುನ) ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆಲಾ ಕೌನ್ಸಿಲ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರೊಗು ಅನುಮತಿ ಕೊರ್ತುಂಡು.<ref name="proUA"/> ಮೇ ೨೦೦೫ಡ್ದ್, ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಅಧಿಕೃತ ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆರ್ನ| ನಿವಾಸ ಆದುಂಡು, ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಉಪಯೋಗ ಆಪುಂಡು.<ref name="vr-340"/><ref>{{Cite web |title=Ukrainian architecture: 10 buildings that everyone should see |url=https://rubryka.com/en/article/architecture-of-ukraine/}}</ref><ref name="BBC Ukrainian"/> ದಿ ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್ ನ್ ಉಕ್ರೇನಿಯನ್ ಅಧ್ಯಕ್ಷೆರ್ ವಿಕ್ಟರ್ ಯುಶ್ಚೆಂಕೊ ಬೊಕ್ಕ ರಷ್ಯಾದ ಅಧ್ಯಕ್ಷೆರ್ ವ್ಲಾಡಿಮಿರ್ ಪುಟಿನ್ ನಡುಟು ಒಂಜಿ ಸಭೆತ ಜಾಗೆ ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್, ಅಪಗ ಚಿಮೇರಾಸ್ ಡಿಸೆಂಬರ್ ೨೨, ೨೦೦೬ಡ್ ಕೀವ್ ಗ್ ಭೇಟಿ ಕೊರ್ತೆರ್.<ref>{{cite news|url=http://www.korrespondent.net/main/174172 |title=Yushchenko and Putin decided their goals for two years ahead|access-date=2007-07-12|date=2006-12-22|work=[[Korrespondent]]|publisher=Bigmir-Internet|language=ru}}</ref> ಕಟ್ಟಡೊಡು ಮಾತುಕತೆ, ಪಾತೆರ, ಅಧಿಕೃತ ದಾಖಲೆಲೆಗ್ ಸಹಿ ಪಾಡುನ ಕೊಠಡಿಲು, ಅಂಚನೆ ಪತ್ರಿಕೆಲೆಗ್ ವಿಶೇಷ ಕೊಠಡಿ ಸೇರ್ದ್ ಉಂಡು.<ref name="president.gov.ua"/> ==ವಾಸ್ತುಶಿಲ್ಪ== [[File:Architectural details on House with Chimaeras 2007.JPG|thumb|250px|ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ವಿನ್ಯಾಸಗೊಳಿಸಾಯಿನ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಉಭಯಚರೊಲು ಬೊಕ್ಕ ಬೇತೆ ಜೀವಿಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರತಿಮೆಲೆನ ವಿವರವಾದ್ ನೋಟ]] {| cellpadding="1" style="float:left; border:1px solid #8888aa; padding:5px; font-size:95%; margin:0 15px 0 0; background:#f9f9f9; width:200px;" |- | architectural_style=" text-align: center;" |ವೊಲೊಡಿಮಿರ್ ಯಾಸಿಯೆವಿಚ್ <ref name="wood">{{harvnb|Yasiievych|1988|p=93.}}</ref> |- |<small>ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಇಲ್ಲ್ ನ್ ಆಂಟೋನಿ ಗೌಡಿ ನ ಕೆಲಸೊಲೆನೊಟ್ಟುಗು ಮಾತ್ರ ಹೋಲಿಕೆ ಮಲ್ಪೊಲಿ, ವಿಶೇಷವಾದ್ ಬಾರ್ಸಿಲೋನಾ, ಸ್ಪೇನ್ ಡ್ ಪ್ರಸಿದ್ಧ ಕಾಸಾ ಮಿಲಾ, ಒಂಜಿ ರಡ್ಡ್ ವರ್ಷೊಡ್ದು ಬೊಕ್ಕ (೧೯೦೫-೧೯೧೦) ನಿರ್ಮಾಣ ಆಯಿನ ನಡುಟು.</small> |} ಈ ಕಟ್ಟಡೊನು ಆರ್ಟ್ ನೌವಿಯೊ ಶೈಲಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಉಂದು ಆ ಕಾಲೊಡು ತುಲನಾತ್ಮಕವಾದ್ ಪೊಸ ಶೈಲಿ ಆದಿತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಹರಿಯುನ, ವಕ್ರರೇಖೆದ ವಿನ್ಯಾಸೊಲೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್, ಉಂದೆಟ್ ಮಸ್ತ್ ಸರ್ತಿ ಪುಷ್ಪ ಬೊಕ್ಕ ಬೇತೆ ಸಸ್ಯ-ಪ್ರೇರಿತ ಮೋಟಿಫ್ ಲೆನ್ ಸೇರಾವೊಂದಿತ್ತೆರ್. ಹೊರೊಡೆಕಿ ಕಟ್ಟಡದ ಪಿದಯಿದ ಅಲಂಕಾರೊಡು ಪೌರಾಣಿಕ ಜೀವಿಲೆನ ಬೊಕ್ಕ ಮಲ್ಲ ಆಟದ ಪ್ರಾಣಿಲೆನ ರೂಪೊಡು ಇಂಚಿನ ಮೋಟಿಫ್ ಲೆನ್ ತೋಜಾದ್ ಕೊರ್ತೆರ್. ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮಲ್ತಿನ ಇಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಬೇಲೆಡ್ದ್ ಕೀವ್ ದ ಆಂಟೋನಿ ಗೌಡಿ ಪನ್ಪಿನ ಅಡ್ಡ ಪುದರ್ ನ್ ಗಳಸೊಂಡೆರ್.<ref name="mirtu"/><ref name="element"/> ಕಟ್ಟಡ ಉಪ್ಪುನ ಕಡಿಮೆ ಇಳಿಜಾರುಡ್ದ್, ಅಯಿತ ಅಡಿಪಾಯೊಗು ಸರಿಯಾದ್ ಸರಿ ಆಯೆರೆ ಕಾಂಕ್ರೀಟ್ ಡ್ದ್ ವಿಶೇಷವಾದ್ ವಿನ್ಯಾಸಗೊಳಿಸೊಡಾಪುಂಡು. ಎದುರುಡ್ದ್ ತೂನಗ, ಕಟ್ಟಡೊಗು ಮೂಜಿ ಮಹಡಿಲು ಮಾತ್ರ ಉಪ್ಪುನಂಚ ತೋಜುಂಡು. ಆಂಡಲಾ, ಪಿರವುಡ್ದ್, ಅಯಿತ ಆಜಿ ಮಹಡಿಲೆನ್ ತೂವೊಲಿ.<ref name="vlasenko">{{harvnb|Zharikov|1983–1986|p=38.}}</ref>ಕಟ್ಟಡದ ಅಡಿಪಾಯೊದ ಒಂಜಿ ಭಾಗೊನು ಕಾಂಕ್ರೀಟ್ ರಾಶಿಲೆಡ್ ಮಲ್ತೆರ್, ಬೊಕ್ಕೊಂಜಿ ಭಾಗೊನು ನಿರಂತರ ಅಡಿಪಾಯೊದ ರೂಪೊಡು ಮಲ್ತೆರ್. ಸಾಮಾನ್ಯವಾದ್, ಈ ರಡ್ಡ್ ವಿಧಾನೊಲು ಎಡ್ಡೆ ರೀತಿಡ್ ಮಿಶ್ರಣ ಆಪುಜಿ ಆಂಡ ಹೊರೊಡೆಕಿ ಒಂಜಿ ರೀತಿಡ್ ಈ ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆನ್ ಜಯಿಪುನಗ ಯಶಸ್ವಿಯಾಯೆರ್. ಇಟಾಲಿಯನ್ ಶಿಲ್ಪಿ ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ಆಂತರಿಕ ಬೊಕ್ಕ ಬಾಹ್ಯ ಶಿಲ್ಪಕಲೆತ ಅಲಂಕಾರೊಲೆಗ್ ಜವಾಬ್ದಾರರಾದಿತ್ತೆರ್, ಉದಾಹರಣೆಗೆ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಡಾಲ್ಫಿನ್ ಲು, ಬೊಕ್ಕ ಕಟ್ಟಡದ ಛಾವಣಿಡ್ ಕಪ್ಪೆಲು, ಪಿದಯಿದ ಗೋಡೆಲೆಡ್ ಮುಳುಗಡೆ ಆಪಿನ ನೌಕೆಲು ಬೊಕ್ಕ ಬೇಟೆದ ಟ್ರೋಫಿಲು, ಬೊಕ್ಕ ಒಳಾಂಗಣ ಅಲಂಕಾರೊಲು, ಉದಾಹರಣೆಗೆ ಮೆಟ್ಟಿಲುಲು ಬೊಕ್ಕ ಮಲ್ಲ ವೇಲು ಚಾಂಡೆಲಿಯರ್ಲು ನಿಂಫೆಯಾ/ಕಮಲ ಪುಷ್ಪೊಲೆನ ಕಾಂಡೊಲೆಡ್ ಕಚ್ಚಿದ್ ದೀತಿನ ಮಲ್ಲ ಕ್ಯಾಟ್ ಫಿಶ್ನ್ ಚಿತ್ರಿಸವುಂಡು. ಸಾಲಾ ಮಲ್ತಿನ ಪಿದಯಿದ ಶಿಲ್ಪೊಲು [[ಸಿಮೆಂಟ್]] ಡ್ದ್ ಮಲ್ಪುನವು. ಸಿಮೆಂಟ್ ದ ಉತ್ಪಾದನೆ «ಫಾರ್» ಕಂಪೆನಿಡ್ ಇತ್ತ್ಂಡ್, ಅಯಿಟ್ ಹೊರೊಡೆಕಿ ಸಹ-ನಿರ್ದೇಶಕೆರ್ ಆದಿತ್ತೆರ್.<ref name="klymenko">{{cite web|url=http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|archive-url=https://web.archive.org/web/20050310062635/http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|url-status=dead|archive-date=2005-03-10|title=Budynok z Khimeramy|access-date=2006-09-30|first=Sergiy|last=Klymenko|work=Photos of Kyiv|language=uk, ru}}</ref> ಕಂಪೆನಿದ ಮುಖ್ಯ ನಿರ್ದೇಶಕೆರ್ ರಿಚ್ಟರ್ ನ ಕೋರಿಕೆದ ಪ್ರಕಾರ ಸಿಮೆಂಟ್ ನ್ ಪ್ರಾಥಮಿಕ ಕಟ್ಟಡ ಸಾಮಗ್ರಿ ಆದ್ ಮಾತ್ರ ಬಳಕೆ ಮಲ್ತೆರ್.<ref>{{cite web|url=http://www.narodnapravda.com.ua/travel/468d12ca51334/|title=Walks around Kyiv. House with Chimaeras|access-date=2006-07-14|author=explorer|date=2007-07-05|work=Narodna pravda|language=uk|archive-date=2011-07-25|archive-url=https://web.archive.org/web/20110725230507/http://www.narodnapravda.com.ua/travel/468d12ca51334/|url-status=dead}}</ref>ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಸಿಮೆಂಟ್ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿ ಆದ್ ಜನಪ್ರಿಯ ಆದಿಜ್ಜಿ, ಅಂಚಾದ್ ಅಯಿತ ಬಳಕೆನ್ ಇಲ್ಲ್ ಬೊಕ್ಕ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿಲೆಗ್ ಪ್ರಚಾರೊದ ರೂಪೊಡು ಬಳಕೆ ಮಲ್ತೆರ್.<ref name="klymenko"/> <gallery class="center" widths=250 heights=185> File:Architectural details on House with Chimaeras 2007-2.JPG|ಜಿಂಕೆ ಬೊಕ್ಕ ಖಡ್ಗಮೃಗಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ವಿಸ್ತಾರವಾದ್ ಸಿಮೆಂಟ್ ಅಲಂಕಾರೊಲು File:Elephant on House with Chimaeras.JPG|ಈ ಕಟ್ಟಡೊಡು ಬೇತೆ ಬೇತೆ ಕೆತ್ತನೆಲು ಉಂಡು, ಉದಾಹರಣೆಗೆ ಈ ಆನೆ-ತಲೆತ ಗಾರ್ಗೊಯ್ಲ್ File:Architectural details on House with Chimaeras 2007-3.JPG|ಕಟ್ಟಡೊದ ಸುತ್ತ ತೂಯಿನ ಮಸ್ತ್ ಮೂರ್ತಿಲೆಡ್ ಒಂಜಿ </gallery> ===ನೆಲತ ಯೋಜನೆ=== [[File:House with Chimaeras (Plan).JPG|thumb|ಆಜನೇ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅರೆನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಹೊರೊಡೆಕಿನ ಮೂಲ ನೀಲನಕ್ಷೆಲು; ೧೯೦೦ ದಶಕದ ಸುರುಟು]] ಚಿಮೇರಾಸ್ ನ ಇಲ್ಲ್ ನ್ ಬಾಡಿಗೆದಾರೆರ್ ಇಡೀ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ಪುನಂಚಿನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಡ್ ಖಾಸಗಿ ಅಡುಗೆಮನೆಡ್ದ್ ಎಲ್ಯ ಪುಡಿ ಕೊಠಡಿಲೆಗ್ ಮುಟ್ಟ ಅಗತ್ಯವಾಯಿನ ಮಾತಾ ಇಲ್ಲದ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಕಟ್ಟಡೊದ ಉಲಯಿ ಉಪ್ಪುನ ತೆರೆದ ನೆಲತ ಯೋಜನೆ ಬೊಕ್ಕ ಹೆಚ್ಚುವರಿ ಕೊಠಡಿಲು 20ನೆ ಶತಮಾನೊದ ಸುರುತ ಶ್ರೀಮಂತೆರೆನ ಇಲ್ಲ್ ಲೆನ ಲಕ್ಷಣೊಲು.<ref name="vlasenko"/> ಒಟ್ಟುಗು, ಕಟ್ಟಡೊದ ವಿಸ್ತೀರ್ಣ {{convert|3309.5|m2|sqft|2|abbr=on}} ಉಂಡು.<ref name="vr-829-р"/> ಗುಡ್ಡೆದ ಆಳೊಡು ಇಪ್ಪುನ ಕಟ್ಟಡೊದ ಕೆಳಮಟ್ಟೊಡು ರಡ್ಡ್ ಸ್ಟೆಬಲ್ಲು, ಕೋಚ್ ಮ್ಯಾನ್ಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಪಾಲ್ ಪಡೆಯಿನ ಬಟ್ಟೆ ಒಯ್ಪುನ ಜಾಗೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಪ್ರತ್ಯೇಕ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್. ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಸ್ಟೋರೇಜ್ ರೂಮ್ ಇತ್ತ್ಂಡ್. ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಸುರುತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ರಡ್ಡ್ ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್, ಬೊಕ್ಕ ರಡ್ಡನೆಡ್ ಮೂಜಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್.{{refn|೧೯೦೩ಡ್ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ ೫೪೦ ಬೊಕ್ಕ ೪೨೦ ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್, ಅಯಿಕ್ ಅನುಗುಣವಾದ್.{{sfn|Malakov|1999}}|group=nb}} ಕೆಳಮಟ್ಟೊದ ಮಿತ್ತ್ ದ ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿನ್ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಮಾತ್ರ ದೀಯೆರೆ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ರಡ್ಡನೆ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಫೋಯರ್, ಅಡುಗೆಮನೆ, ಬಫೆಟ್, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ಆರು ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಒಂಜೇ ಮಟ್ಟೊಡು ನಾಲ್ ವೈನ್ ಸೆಲ್ಲರ್ ಲಾ ಇತ್ತ್ಂಡ್.{{refn|Tಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಬೆಲೆ 1903ಡ್ 1,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ನೆಲಮಾಳಿಗೆಲು ಮಿತ್ತ್ ದ ಮಟ್ಟೊಡು ಉಪ್ಪುನ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲೆಗ್ ಸೇರ್ದಿನವು. ಮೂಜಿನೆ ಮಹಡಿಡ್, ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಎಂಟು ವಸತಿ ಕೊಠಡಿಲು, ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಸ್ಕಲ್ಲರಿ (ಕೋಣೆ)/ಪಾತ್ರೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಸೇವಕೆರೆಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು ಇತ್ತ್ಂಡ್.{{refn|ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸುರುತ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ 2,000 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಬಂಕೋವಾ ಸ್ಟ್ರೀಟ್ ದ ಮಟ್ಟೊಡ್ದ್ ಸ್ವಲ್ಪ ಕೆಳಗ್ ದೀಯೆರ್, ಎದುರುದ ಪ್ರವೇಶ ದ್ವಾರೊಡ್ದ್. ಹೊರೊಡೆಕಿಗ್ ಸೇರ್ದಿನ ಮಲ್ಲ ಅಪಾರ್ಟ್ಮೆಂಟ್, ಒಂಜಿ ಅಧ್ಯಯನ (ಕೋಣೆ), ಒಂಜಿ ಮಲ್ಲ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಲಿವಿಂಗ್ ರೂಮ್, ಒಂಜಿ ಡೈನಿಂಗ್ ರೂಮ್, ಒಂಜಿ ಬೌಡೋಯಿರ್, ಒಂಜಿ ಬೆಡ್ ರೂಮ್, ಒಂಜಿ ಜೋಕುಲೆನ ಕೋಣೆ, ಒಂಜಿ ಗವರ್ನೆಸ್ ಗ್ ಒಂಜಿ ಕೋಣೆ, ಒಂಜಿ ಅತಿಥಿ ಕೋಣೆ, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೋಣೆ, ಅಡುಗೆಮನೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಒಂಜಿ ಕೋಣೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲು.<ref name="Horodetskyi"/> ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಹೊರೊಡೆಕಿನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸಮಾನವಾದ್ ಗಾತ್ರ ಬೊಕ್ಕ ವಿನ್ಯಾಸೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಇತ್ತ್ಂಡ್.{{refn|ಈ ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆನ್ 1903ಡ್ ವಾರ್ಷಿಕ 3,500 ರೂಬಲ್ಸ್ ಗ್ ಬಾಡಿಗೆಗ್ ಕೊರ್ತೆರ್.{{sfn|Malakov|1999}}|group=nb}} ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಕೋಣೆ ಕಮ್ಮಿ ಇತ್ತ್ಂಡ್; ಇಂದೆನ್ ಸರಿ ಮಲ್ಪರೆ, ನಗರೊದ ವಿಹಂಗಮ ದೃಶ್ಯೊನು ಒದಗಾವುನ ಒಂಜಿ ಸಂಪರ್ಕ ಟೆರೇಸ್ ಇತ್ತ್ಂಡ್.<ref name="Horodetskyi"/>{{refn|The rental cost for this dwelling was 2,750 rubles annually. For comparison, an average salary for a librarian was about 50 rubles per year.{{sfn|Ivanenko|2004}}|group=nb}} ==ದಂತಕಥೆಲು== [[File:House with Chimaeras gardens.JPG|thumb|ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಉದ್ಯಾನೊಲು ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡ (ಕೀವ್) ಹಿನ್ನೆಲೆಡ್ ತೋಜುಂಡು]] ವರ್ಷೊಡ್ದಿಂಚಿ, ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಪ್ಪುನ ಇಲ್ಲ್ ದ ಅಸಾಮಾನ್ಯ ಸ್ವಭಾವೊ ಮಾರ್ಗದರ್ಶಿ-ಪುಸ್ತಕೊಲೆಡ್ ಅತ್ತಂದೆ ಪತ್ರಿಕೆಲೆಡ್ ಕೆಲವೊಂಜಿ ಸರ್ತಿ ಪುನರಾವರ್ತಿತವಾದ್ ಬರ್ಪಿನ ಒಂಜಿ ಸಂಖ್ಯೆದ ಕತೆಕುಲೆಗ್ ಕಾರಣವಾತ್ಂಡ್, ಆಂಡಲಾ ಅವು ಅಸತ್ಯ ಅತ್ತಂದೆ ಯಾವುದೇ ಪರಿಶೀಲಿಸಬಹುದಾದ ಮೂಲೊನು ಕಳೆವೊಂದಿಜ್ಜಿ.<ref>{{harvnb|Savchuk|1996|p=95.}}</ref> ಸುರುತ ದಂತಕಥೆದ ಪ್ರಕಾರ, ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಮಗೆ ಕೆಲವು ದುರದೃಷ್ಟಕರ ಪ್ರೇಮ ಸಂಬಂಧೊಡ್ದ್ ಅತ್ತಂಡ ಕುಟುಂಬದ ಜಗಳೊಡ್ದ್ ಡ್ನಿಪರ್ ನದಿಗ್ ಜಂಪ್ ಮಲ್ತ್ ದ್ ಆತ್ಮಹತ್ಯೆ ಮಲ್ತೆರ್.{{refn|It was actually previous owner's ([[Friedrich Mering|Professor Mering]]) daughter who drowned.{{sfn|Ivanenko|2004}}|group=nb}}ಫಲವಾದ್, ಹೊರೊಡೆಕಿ ಸ್ವಲ್ಪ ಪಿಚ್ಚರ್ ಆದ್ ತನ್ನ ಮಗೆನ ನೆಂಪುಗು ಈ ಕತ್ತಲೆದ ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="kievinfo">{{cite web|url=http://www.kiev.info/culture/house_chimeras.htm|title=House with Chimaeras|access-date=2006-02-16|first=Viktor|last=Pavlovsky |work=Kiev Info|publisher=Optima Tours}}</ref> ರಡ್ಡನೆ ದಂತಕಥೆ ಪಂಡ ಹೊರೊಡೆಕಿ ಕೆಲವು ಬೇತೆ ವಾಸ್ತುಶಿಲ್ಪಿಲೆನೊಟ್ಟುಗು ಒಂಜಿ ಪಂತೊನು ಮಲ್ತೆರ್, ವಾಸ್ತುಶಿಲ್ಪಿ ಅಲೆಕ್ಸಾಂಡರ್ ಸ್ಕೋಬೆಲೆವ್ ಸೇರ್ದ್, ಅಂಚನೆ ಭೂಪ್ರದೇಶೊಡು ಒಂಜಿ ಇಲ್ಲ್ ಕಟ್ಟೆರೆ ಅಸಾಧ್ಯ ಪನ್ಪಿನೆನ್ ಸಾಬೀತು ಮಲ್ಪೆರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್, ದಾಯೆ ಪಂಡ ಆ ಜಾಗೆ (ಇವಾನ್ ಫ್ರಾಂಕೊ ಥಿಯೇಟರ್ ದ ಸಮೀಪೊಡು) ಒಂಜಿ ಜವುಗು (''ಕೊಜ್'ಯೆ ಬೊಲೊಟೊ'') ಮಿತ್ತ್ ಉಂಡು. ಕೀವ್ ದ ನಿರ್ಮಾಣ ಸಮಿತಿ ಈ ನಿರ್ದಿಷ್ಟ ಲಾಟ್ ಡ್ ಏರ್ಲಾ ರಚನೆಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ನಿಷೇಧ ಮಲ್ತ್ಂಡ್, ಆಂಡ ಅಂಚನೆ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಡು ಹೊರೊಡೆಕಿ ಪಂತೊನು ಗೆಂದಿಯೆರೆ ಅನುವು ಮಲ್ತೆರ್.<ref name="kievinfo"/> ಮೂಜಿನೆ ದಂತಕಥೆದ ಪ್ರಕಾರ, ಹೊರೊಡೆಕಿ 1913ಡ್ ಶಾಪ ಕೊರ್ತೆರ್ (ಅರೆನ ಫೋರ್ಕ್ಲೋಸರ್/ಅರೆನ ಸಾಲಗಾರೆರೆಗ್ ಮರುಪಾವತಿ ಮಲ್ಪೆರೆ ಅಸಮರ್ಥತೆ ಕಾರಣ); ಇಲ್ಲ್ ದ ಮಾತಾ ಬಾಡಿಗೆದಾರೆರ್ ದುಖಿಡ್ ಉಪ್ಪುವೆರ್ ಅತ್ತ್ಂಡ ಒಂಜಿ ರೀತಿದ ಆರ್ಥಿಕ ದುರದೃಷ್ಟೊನು ಎದುರಿಸವೆರ್. ಕಟ್ಟಡೊದ ಒಂಜಿ ಭಾಗೊನು ಬಾಡಿಗೆಗ್ ದೆತೊನುನ ಮಾತಾ ವ್ಯವಹಾರೊಲು ಒಂಜಿ ವೇಳೆ ದಿವಾಳಿ ಪೋಯೆರ್, ಅಕ್ಲೆನ ನಿಧಿನ್ ಕಳವು ಮಲ್ತೆರ್ ಅತ್ತ್ಂಡ ರದ್ದು ಮಲ್ತೆರ್ ಪನ್ಪಿನ ಒಂಜಿ ಕತೆ ಉಂಡು.<ref name="mirtu"/> == ಉಲ್ಲೇಕೊಲು == === ಟಿಪ್ಪಣಿಲು === {{Reflist|group="nb"|30em}} === ಅಡಿಟಿಪ್ಪಣಿಲು === {{Reflist|30em}} === ಗ್ರಂಥಸೂಚಿ === {{Refbegin}} * {{Cite journal|last=Ivanenko|first=Anna|title=Horodecki. Lord of Chimaeras|url=http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|volume=1–2|journal=Mir Turizma|year=2004|location=[[Kyiv]]|issn=1811-3583|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20070506082208/http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|archive-date=2007-05-06}} * {{Cite book|last=Malakov|first=Dmytro|title=Arkhitektor Horodetskyi|publisher=Kyi|year=1999|location=[[Kyiv]]|isbn=966-7161-30-7|language=uk}} * {{Cite book|last=Malikenaite|first=Ruta|title=Touring Kyiv|publisher=Baltija Dryk|year=2003|isbn=966-96041-3-3|language=uk}} * {{Cite book|last=Savchuk|first=Galina|title=The Streets of Kiev: Five Walks in the Centre|publisher=ArtEc Publishers|year=1996|location=[[Kyiv]]}} * {{Cite book|last=Yasiievych|first=V. E.|title=Architecture of Ukraine at the Turn of the 19th–20th Centuries|publisher=Budivel'nyk|year=1988|location=[[Kyiv]]|isbn=5-7705-0088-3|oclc=19693625|language=ru}} * {{Cite book|last=Zharikov|first=N. L.|title=Monuments of Urban Development and Architecture in the Ukrainian SSR|chapter=Residential house (Horodecki's), 1904|chapter-url=http://ua.vlasenko.net/_pgs/pgs-html/pgs1-038.html|volume=1–4|publisher=Budivel'nyk|year=1983–1986|location=[[Kyiv]]|language=ru|lccn=84179019}} {{Refend}} ==ಬಾಹ್ಯ ಕೊಂಡಿಲು== {{Commons category|House with Chimeras (Kyiv)}} * {{Cite web|url=http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|title=Art Nouveau in Kyiv|first=Dmytro|last=Horbachov|access-date=2006-09-30|archive-url=https://web.archive.org/web/20041104061652/http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|archive-date=2004-11-04|url-status=dead|ref=none}} * {{Cite encyclopedia|url=http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|title=House with Chimaeras|encyclopedia=Wiki-Encyclopedia Kyiv|language=uk|access-date=2011-06-21|archive-date=2022-03-07|archive-url=https://web.archive.org/web/20220307105847/http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|url-status=dead}} * {{Cite web|url=http://www.interesniy.kiev.ua/old/architecture/buildings/2 |script-title=ru:Замки Киева: Легенды и были Замка Ричарда, Дом с Химерами, замок барона Штейнгеля|trans-title=Castles of Kiev: Legends were Richard's Castle, the House of Chimeras, Baron Steingel's castle |work=Interesniy Kiyev |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20061104033503/http://www.interesniy.kiev.ua/old/architecture/buildings/2 |archive-date=2006-11-04 }} * {{Cite web|url=http://brmr.livejournal.com/628752.html |title=Photogallery containing detailed images |publisher=[[LiveJournal]] |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20130814171250/http://brmr.livejournal.com/628752.html |archive-date=2013-08-14 }} * {{Cite web|url=http://www.domparketa.com.ua/ru/gallery/dom_ximer/ |title=Interior photogallery |work=domparketa.com.ua |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20060829142840/http://domparketa.com.ua/ru/gallery/dom_ximer/ |archive-date=2006-08-29 }} ohw6cwt74sx7rmm9sy570957xhqfhoj 366214 366204 2026-08-21T11:49:40Z Hariprasad Shetty10 6127 366214 wikitext text/x-wiki {{Short description|ಕೀವ್, ಉಕ್ರೇನ್‌ಡ್ ಇಪ್ಪುನ ಆರ್ಟ್ ನುವೊ ವಾಸ್ತುಶಿಲ್ಪೊದ ಕಟ್ಟಡ}} {{Infobox building | name = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್ | native_name = Будинок з химерами | native_name_lang = uk | image = Будинок із химерами-3.JPG | image_alt = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ದೃಶ್ಯ | image_caption = ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್‌ದ ಮುಂಭಾಗೊದ ಮುಖಭಾಗ | architectural_style = ಆರ್ಟ್ ನುವೊ | structural_system = ಕಾಂಕ್ರೀಟ್ ಪೈಲುಲು; ನಿರಂತರ ಅಡಿಪಾಯ | location = ಲಿಪ್ಕಿ, ಕೀವ್, ಉಕ್ರೇನ್ | address = 10, ಬ್ಯಾಂಕೋವಾ ಬೀದಿ | coordinates = {{coord|50|26|42|N|30|31|43|E|type:landmark_region:UA-30|display=inline,title}} | construction_start_date = 1901 | completion_date = 1902 | floor_count = 3 (ಬ್ಯಾಂಕೋವಾ ಬೀದಿ ಬದಿಟ್)<br>6 (ಫ್ರಾಂಕೊ ಚೌಕ ಬದಿಟ್) | floor_area = {{convert|3309.5|m2|sqft|2|abbr=on}} | architect = ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರಡೆಟ್ಸ್ಕಿ }} '''ಚಿಮೇರೊಲೆನ್ ಇಲ್ಲ್''' ಅತ್ತುಡ '''ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್''' ಅತ್ತುಡ '''ಹೊರೊಡೆಟ್ಸ್ಕಿ ಹೌಸ್''' (ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ಅರೆನ ಪುದರ್ ಡ್ ಪುದರ್ ದೀತಿನ) ಆರ್ಟ್ ನೌವಿಯೊ ಕಟ್ಟಡ. ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆನ ಕಚೇರಿದ ಬೀದಿದ ಎದುರುಡು ಉಪ್ಪುನ ಈ ಕಟ್ಟಡೊನು 2005 ಡ್ದ್ ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಅಧ್ಯಕ್ಷೆರೆನ ನಿವಾಸೊ ಆದ್ ಬಳಕೆ ಮಲ್ತೊಂದು ಬೈದೆರ್.<ref name="vr-340">{{Cite Ukrainian law|type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=340|law=340-2005-п|name=On the addition to the house on 10, Bankova St. in the city of Kyiv the status of a government residence|date=2005-05-11}}</ref><ref name="BBC Ukrainian">{{cite news|url=https://www.bbc.com/ukrainian/indepth/story/2005/04/050401_ukraine_residences |title=The aura is fine, but the place is too small|access-date=2006-09-30|first=Marta|last=Shokalo|date=2005-04-01|publisher=[[BBC News|BBC Ukrainian]]|language=uk}}</ref> == ಸ್ಥಳ ಬೊಕ್ಕ ಇತ್ತೆದ ಬಳಕೆ == ಕಟ್ಟಡೊದ ಎದುರುದ ಬೀದಿನ್ ಮಾತಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ಗ್ ಮುಚ್ಚಿದ್ ದೀತೆರ್, ಬೊಕ್ಕ ಇತ್ತೆ ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡಗ್ ಕೈತಲುಡು ಉಪ್ಪುನೆರ್ದಾತ್ರ ಉಂದು ಒಂಜಿ ಗಸ್ತು ಪಾದಚಾರಿ ವಲಯ ಆದುಂಡು. 2005 ಡ್ದ್ ಈ ಕಟ್ಟಡೊನು ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಅಧ್ಯಕ್ಷೆರೆನ ನಿವಾಸೊ ಆದ್ ಗಳಸೊಂದು ಬೈದೆರ್.<ref name="vr-340"/><ref name="BBC Ukrainian"/> == ನಿರ್ಮಾಣ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಿ == ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ಮೂಲತಃ 1901–1902 ಡ್ ತನ್ನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಕಟ್ಟಡೊ ಆದ್ ಬಳಕೆ ಮಲ್ಪುನ ನಿಟ್ಟಿಡ್ ಚಿಮೇರಾಸ್ ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.ಹೊರೊಡೆಕಿ ಆರ್ಟ್ ನೌವಿಯೊ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರಭಾವೊನು ಕಟ್ಟಡೊದ ವಿನ್ಯಾಸೊಡು ಬಳಸಿನೆರ್. ಕಟ್ಟಡೊದ ಪಿದಾಯಿದ ಭಾಗೊಡು ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲು, ಪೌರಾಣಿಕ ಜೀವಿಲು ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲೆನ ಅಲಂಕಾರೊಲು ವಿಶೇಷ ಆಕರ್ಷಣೆ ಆದುಂಡು. == ಮಾಲೀಕತ್ವೊದ ಬದಲಾವಣೆಲು == ವರ್ಷೊಲು ಕರಿಲೆಕನೆ, ಹೊರೊಡೆಕಿ ಕಡೆಕ್ ಆರ್ಥಿಕ ತೊಂದರೆಲೆಡ್ದ್ ಕಟ್ಟಡೊನು ಮಾರಾಟ ಮಲ್ಪೊಡಾಂಡ್. ಅಯಿಡ್ದ್ ಬೊಕ್ಕ ಕಟ್ಟಡೊ ಅನೇಕ ಸರ್ತಿ ಮಾಲೀಕತ್ವೊನು ಬದಲಾವಣೆ ಮಲ್ತ್ಂಡ್.2000 ದ ಸುರುತ ಮುಟ್ಟ ಉಕ್ರೇನ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಅಧಿಕೃತ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಕ್ರಮಣ ಮಲ್ಪುನ ದುಂಬು ಕಟ್ಟಡೊ ಬೇತೆ ಬೇತೆ ಬಳಕೆಲೆಗ್ ಒಳಪಟ್ಟಿತ್ತ್ಂಡ್.<ref name="riddle"/> == ಪುನರ್ ಸ್ಥಾಪನೆ == ಕಟ್ಟಡೊನು ಖಾಲಿ ಮಲ್ತಿನಗ, ಅಯಿತ ಒಳಾಂಗಣ ಬೊಕ್ಕ ಪಿದಾಯಿದ ಅಲಂಕಾರೊನು ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆನ್ ಅನುಸರಿಸಾದ್ ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆ ಮಲ್ತೆರ್.<ref name="mirtu">{{harvnb|Ivanenko|2004}}</ref> ಪುನರ್ ಸ್ಥಾಪನೆದ ಪಿರವುಟು ಕಟ್ಟಡೊದ ಮೂಲ ವಾಸ್ತುಶಿಲ್ಪೀಯ ರೂಪ ಬೊಕ್ಕ ಅಲಂಕಾರೊನು ಸಾಧ್ಯವಾದಷ್ಟು ಉಳಿಸಾದ್ ದೀತೆರ್. ಉಂದು ಕೀವ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಕಟ್ಟಡೊಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. == ಚಿಮೇರಾಸ್ ಪುದರ್ ಬತ್ತಿನ ಕಾರಣ == ಹೊರೊಡೆಕಿ ಒರಿ ಉತ್ಸಾಹಿ ಬೇಟೆಗಾರೆ ಆಯಿನೆಡ್ದಾವರ, ಇಟಾಲಿಯನ್ ವಾಸ್ತುಶಿಲ್ಪಿ ಬೊಕ್ಕ ಶಿಲ್ಪಿ '''ಎಮಿಲಿಯೊ ಸಾಲಾ''' ಕಟ್ಟಡೊದ ಅಲಂಕಾರೊಲೆಗ್ ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲೆನ್ ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲೆನ್ ಬಳಸಿನೆರ್.<ref name="vinnytsia_library"/> ಈ ವಿಶೇಷ ಅಲಂಕಾರೊಲೆಡ್ದ್ ಈ ಕಟ್ಟಡೊಗು '''ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್''' ಪುದರ್ ಬತ್ತ್ಂಡ್. == ಚಿಮೇರಾಸ್ ಅತ್ತಂದೆನ ಅರ್ಥ == ಈ ಪುದರ್ ಪುರಾಣೊದ ಚಿಮೇರಾ ಜೀವಿನ್ ನೇರವಾದ್ ಸೂಚಿಸಾವುಂಡು. ಬದಲಾದ್, ಕಟ್ಟಡೊದ ವಾಸ್ತುಶಿಲ್ಪೊಡು ಪ್ರಾಣಿಲು ಬೊಕ್ಕ ಪೌರಾಣಿಕ ಜೀವಿಲೆನ ವಿಚಿತ್ರ ರೂಪೊಲೆನ್ ಅಲಂಕಾರಿಕ ಅಂಶೊಲೆನ್ ಆದ್ ಬಳಸುನ ಶೈಲಿನ್ ಸೂಚಿಸಾವುಂಡು. ಕಟ್ಟಡೊದ ಗೋಡೆಲು, ಮೇಲ್ಚಾವಣಿ ಬೊಕ್ಕ ಮುಖಭಾಗೊಡು ವಿವಿಧ ಪ್ರಾಣಿಲೆನ ಶಿಲ್ಪೊಲು ಇಪ್ಪುನ ಕಾರಣೊಡು ಉಂದು ಕೀವ್‌ದ ವಿಶಿಷ್ಟ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಕಟ್ಟಡೊಲೆಡ್ ಒಂಜಿ ಆದ್ ಪರಿಗಣಿಸುಂಡು. == ಎಮಿಲಿಯೊ ಸಾಲಾ ಬೊಕ್ಕ ಶಿಲ್ಪಕಲೆ == ಇಟಾಲಿಯನ್ ವಾಸ್ತುಶಿಲ್ಪಿ ಬೊಕ್ಕ ಶಿಲ್ಪಿ '''ಎಮಿಲಿಯೊ ಸಾಲಾ''' ಕಟ್ಟಡೊದ ಅಲಂಕಾರಿಕ ಶಿಲ್ಪೊಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರೆ ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಶಿಲ್ಪೊಲೆಡ್ ವಿಲಕ್ಷಣ ಪ್ರಾಣಿಲು, ಸಮುದ್ರ ಜೀವಿಲು ಬೊಕ್ಕ ಬೇಟೆದ ದೃಶ್ಯೊಲು ಮುಖ್ಯ ಆದುಂಡು.<ref name="vinnytsia_library"/> ಈ ಶಿಲ್ಪೊಲು ಕಟ್ಟಡೊದ ಆರ್ಟ್ ನೌವಿಯೊ ವಿನ್ಯಾಸೊಗು ಒಂಜಿ ವಿಶಿಷ್ಟ ರೂಪೊನು ಕೊರ್ತುಂಡು. == ವಾಸ್ತುಶಿಲ್ಪೀಯ ಪ್ರಭಾವೊ == ಹೊರೊಡೆಕಿನ ಅನನ್ಯ ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿ ಕೀವ್‌ಡು ಮಾತ್ರ ಅತ್ತಂದೆ ಅಂತರರಾಷ್ಟ್ರೀಯ ಮಟ್ಟೊಡುಲಾ ಗಮನ ಸೆಳೆದ್ಂಡ್. ಅರೆನ ಕಟ್ಟಡೊದ ವಿನ್ಯಾಸೊನು ಸ್ಪೇನ್‌ದ ಪ್ರಸಿದ್ಧ ವಾಸ್ತುಶಿಲ್ಪಿ ಆಂಟೋನಿ ಗೌಡಿದ ಶೈಲಿದೊಟ್ಟುಗು ಹೋಲಿಸಾಯಿನವುಲಾ ಉಂಡು.<ref name="mirtu">{{harvnb|Ivanenko|2004}}</ref><ref name="element">{{cite news|url=http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |title=heed the krai |access-date=2006-09-30 |first=Sonya |last=Rinkus |work=elementmoscow.ru |url-status=dead |archive-url=https://web.archive.org/web/20071109061542/http://elementmoscow.ru/articles.php?i=193001&s=05-getaways |archive-date=2007-11-09 }}</ref> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವೊ == ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್ ಕೀವ್‌ದ ಆರ್ಟ್ ನೌವಿಯೊ ವಾಸ್ತುಶಿಲ್ಪದ ಒಂಜಿ ಪ್ರಮುಖ ಉದಾಹರಣೆ ಆದುಂಡು. ಕಟ್ಟಡೊದ ವಿಶಿಷ್ಟ ಶಿಲ್ಪೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರೊಲು ಉಕ್ರೇನ್‌ದ ರಾಜಧಾನಿದ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಗುರುತಿನ ಒಂಜಿ ಭಾಗಲಾ ಆದುಂಡು. ==ಇತಿಹಾಸ== ===ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಸುರುತ ಇತಿಹಾಸ=== ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಒಂಜಿ ಇಲ್ಲ್ ನ್ ಪೋಲಿಷ್ ವಾಸ್ತುಶಿಲ್ಪಿ ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿ ೧೯೦೧-೧೯೦೨ಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್.<ref name="mirtu"/> ಹೊರೊಡೆಕಿ ೧೮೬೩ಡ್ ಪೊಡೊಲಿಯಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಸಮೃದ್ಧ ಪೋಲಿಷ್ ಜನಕುಲು ಸ್ಜ್ಲಾಚ್ಟಾ (ಉತ್ತಮ) ಕುಟುಂಬೊಡು ಪುಟ್ಟುದುಪ್ಪುವೆರ್.<ref name="vinnytsia_library">{{cite web|url=http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |title=Architector of century |date=2003-05-20 |access-date=2007-07-14 |publisher=Vinnytsia oblast' universal science library named after K.A.Timiryazev |language=uk |archive-url=https://web.archive.org/web/20070928062123/http://www.library.vinnitsa.com/publications/2003/gorodetskiy.html |archive-date=September 28, 2007 |url-status=dead }}</ref> ೧೮೯೦ಡ್ ಇಂಪೀರಿಯಲ್ ಅಕಾಡೆಮಿ ಆಫ್ ಆರ್ಟ್ಸ್ ನ್ ಸೈಂಟ್ ಪೀಟರ್ಸ್ಬರ್ಗ್ ಮುಗಿತ್ ಬೊಕ್ಕ, ಆರ್ ಕೀವ್ ಗ್ ಪೋಯೆರ್, ಅಲ್ಪ ಆರ್ ಸುಮಾರ್ ೩೦ ವರ್ಸೊಗು ಜೀವನ ಮಲ್ತೆರ್.<ref name="vinnytsia_library"/> ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಹೊರೊಡೆಕಿ ಒರಿ ಪ್ರಮುಖ ಕೀವ್ ವಾಸ್ತುಶಿಲ್ಪಿಯಾದ್ ತನ್ನನ್ ತಾನ್ ಸ್ಥಾಪಿಸೊಂದಿತ್ತೆರ್, ಅರೆನ ನಿಕಟ ಸ್ನೇಹಿತೆ ಬೊಕ್ಕ ಪಾಲುದಾರ ಎಂಜಿನಿಯರ್ ಆಂಟನ್ ಸ್ಟ್ರಾಸ್ ನೊಟ್ಟುಗು ಮಸ್ತ್ ನಗರ ಕಟ್ಟಡೊಲೆನ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್, ಸೆಂಟ್. ನಿಕೋಲಸ್ ರೋಮನ್ ಕ್ಯಾಥೊಲಿಕ್ ಕ್ಯಾಥೆಡ್ರಲ್, ಕೀವ್ ಡ್ದ್ ಕರೈಟೆ ಕೆನೆಸಾ (ಕೀವ್) ಬೊಕ್ಕ ಇನಿ ಉಕ್ರೇನ್ ದ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ಮ್ಯೂಸಿಯಂ. ವಾಸ್ತುಶಿಲ್ಪೊದ ಒಟ್ಟುಗು, ಹೊರೊಡೆಕಿಗ್ ಮಲ್ಲ-ಆಟೊದ ಬೇಟೆ ಡ್ ಆಸಕ್ತಿ ಇತ್ತ್ಂಡ್,<ref name="vinnytsia_library"/> ಉಂದು ಅರೆನ ಕಟ್ಟಡೊಡು ದಾಯೆಗ್ ಮಸ್ತ್ ಪ್ರಾಣಿಲೆನ್ ತೋಜಾವುಂಡು ಪನ್ಪಿನೆನ್ ವಿವರಿಸವುಂಡು. [[File:House with Chimaeras during construction.jpg|thumb|left|ನಿರ್ಮಾಣೊಡು ಉಪ್ಪುನ ಕಟ್ಟಡ, ೧೯೦೨]] ಹೊರೊಡೆಕಿ ಸಾಲದ ದುಡ್ಡುಡ್ದ್ ಇಲ್ಲ್ ದ ನಿರ್ಮಾಣೊಗು ಹಣಕಾಸುದ ವ್ಯವಸ್ಥೆ ಮಲ್ತೆರ್,<ref name="mirtu"/><ref name="KV"/> ಉಂದು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಆವರೆ ಉದ್ದೇಶ ಇತ್ತುಂಡು.<ref name="Horodetskyi">{{harvnb|Malakov|1999|p=?}}</ref><ref name="Touring Kyiv">{{harvnb|Malikenaite|2003|p=50.}}</ref> ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಲಾ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ರೂಪಿಸೊಂದಿತ್ತ್ಂಡ್, ಉಂದೆನ್ ಎಲಿವೇಟರ್ ಬೊಕ್ಕ ಮೆಟ್ಟಿಲುಲೆಡ್ದ್ ಸಂಪರ್ಕ ಮಲ್ದುಂಡು.{{refn|ಉಂದೆಕ್ ಒಂಜೇ ಅಪವಾದ ಪಂಡ ಕಟ್ಟಡೊದ ಕೆಳಮಹಡಿಡ್ ರಡ್ಡ್ ಬೇತೆ ಬೇತೆ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್.|group=nb}} ಹೊರೊಡೆಕಿ ಸ್ವತಃ ಕಟ್ಟಡದ ನಾಲ್ನೇ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ತೆರ್, ಸುಮಾರ್{{convert|380|m2|sqft|abbr=on}}.<ref name="Horodetskyi"/>{{refn|ಅರೆನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಸೂಕ್ತವಾಯಿನವು. ನಂ. 3, ಮುಖ್ಯ ಮಹಡಿ, ಬಂಕೋವಾ ಬೀದಿದ ಮಟ್ಟೊಡ್ದ್ ತೂಂಡ.|group=nb}} ಹೊರೊಡೆಕಿ ಫೆಬ್ರವರಿ ೧, ೧೯೦೧ಡ್ ಸುರುತ ಜಮಿನ್ ನ್ ಕೊಂಡೊಂತೆರ್, ಆ ವರ್ಷದ ಮಾರ್ಚ್ ೧೮ಡ್ ನಿರ್ಮಾಣದ ಬೇಲೆ ಸುರು ಆಂಡ್. ಪಿದಯಿದ ಗೋಡೆಲೆನ ನಿರ್ಮಾಣ ಆಗಸ್ಟ್ ೨೧ ಗ್ ಮುಗಿಂಡ್, ಬೊಕ್ಕ ಛಾವಣಿನ್ ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಮಾತಾ ಕಲ್ಲುದ ಬೇಲೆನ್ ಸೆಪ್ಟೆಂಬರ್ ೧೩ ಗ್ ಮುಗಿತ್ಂಡ್.<ref name="Horodetskyi"/><ref name="Touring Kyiv"/> ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ದ ಒಳಗೆ ಆರ್ಥಿಕ ಕಷ್ಟೊಲೆಡ್ದ್, ಕಟ್ಟಡೊದ ಪೂರ್ಣಗೊಳಿಸುವಿಕೆ ತಡವಾಂಡ್. ಮೇ ೧೯೦೩ಡ್, ಕೆಳಮಟ್ಟೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಬೊಕ್ಕ ಹೊರೊಡೆಕಿನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಮಾತ್ರ ಆಕ್ರಮಣ ಮಲ್ತೆರ್.<ref name="Horodetskyi"/> ಭೂಮಿ ಬೊಕ್ಕ ನಿರ್ಮಾಣದ ಒಟ್ಟು ವೆಚ್ಚ 133,000 ಆಂಡ್ ರಷ್ಯನ್ ರೂಬಲ್.{{refn|ಈ ಬೆಲೆ ೧೯೦೩ ಡ್ದ್ ಉಂಡು.[https://web.archive.org/web/20170323182358/http://www.archives.gov.ua/Archives/index.php?ra27 Government Archive of the City of Kyiv] – [[Fonds|Fond]] No. 143, Series 2, File No. 520, Item No. 9. In the late 19th and early 20th century, one thousand [[Russian ruble]]s were worth about 24.89 [[gold]] [[troy ounce]]s, or roughly one million Russian rubles as of 2011.{{sfn|Malakov|1999}}|group=nb}} ಒಟ್ಟುಗು, {{convert|1550|m2|sqft|abbr=on}} ಜಮಿನ್ ನ್ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಗು ಬಳಕೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಒಟ್ಟು 15,640 ರೂಬಲ್ಸ್ ಖರ್ಚು ಆಂಡ್.<ref name="KV">{{cite web|url=http://old.kv.com.ua/index.php?rub=46&number_old=2984|title=The rebirth of the House with Chimaeras|access-date=2006-07-14|first=Mikhail|last=Kalnitsky|work=Kievskie Vedomosti №179 (2984)|language=ru|date=2003-08-19|archive-date=2018-08-20|archive-url=https://web.archive.org/web/20180820045803/http://old.kv.com.ua/index.php?rub=46&number_old=2984|url-status=dead}}</ref> ಬಾಡಿಗೆಡ್ದ್ ವಾರ್ಷಿಕ ಲಾಭ 7,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್. ಹೊರೊಡೆಕಿ ತಾಜಾ ಇನ್-ಹೌಸ್ ಹಾಲ್ದ ಒತ್ತಾಯೊಗು ಕಾರಣವಾದ್ ಒಂಜಿ ಗೋಕುಲೊನು ಆವರಣೊಡು ದೀತೆರ್,<ref name="mirtu"/><ref name="Touring Kyiv"/> ಗಾಯೊಲೆದ ವಾಸನೆ ಬಾಡಿಗೆದಾರೆರೆಗ್ ತೊಂದರೆ ಆವಂದಿಲೆಕ ನಿರ್ದಿಷ್ಟವಾದ್ ದೀದ್ ಇತ್ತ್ಂಡಲಾ. ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಒಂಜಿ ಜಾಗೆಡ್, ಒಂಜಿ ಎಲ್ಯ ಆಲ್ಪೈನ್ ಗಾರ್ಡನ್ (ಅಂದಾಜಿ. {{convert|320|m2|sqft|-2|abbr=on|disp=or}}) ಬೊಕ್ಕ ಒಂಜಿ ಫೌಂಟೇನ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರೆನ ಸಫಾರಿ ಬೇಟೆದ ಹವ್ಯಾಸೊನು ಒಳಗೊಂಡ ಆರ್ಥಿಕ ದುರ್ಬಳಕೆಡ್ದ್,<ref name="mirtu"/> ಜುಲೈ ೧೯೧೨ಡ್, ಹೊರೊಡೆಕಿ ಕೀವ್ ಮ್ಯೂಚುಯಲ್ ಕ್ರೆಡಿಟ್ ಅಸೋಸಿಯೇಷನ್ ​​ಡ್ದ್ ದೆತೊನುನ ಸಾಲೊಗು ವಿರುದ್ಧವಾದ್ ಕಟ್ಟಡೊನು ಜಾಮೀನು ಆದ್ ವಾಗ್ದಾನ ಮಲ್ತೆರ್.<ref name="riddle">{{cite web|title=Riddle of House with Chimaeras|url=http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|first=Yuliya|last=Ivashko|work=Vash Kiev|access-date=2006-07-14|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20120206003140/http://www.oldkiev.info/progulki_ulicami_Kieva/zagadka_doma_s_ximerami.html|archive-date=2012-02-06}}</ref> ಹೊರೊಡೆಕಿ ಸಾಲೊಡು ಡಿಫಾಲ್ಟ್ ಆನಗ, ಕಟ್ಟಡೊನು ೧೯೧೩ಡ್ ಹರಾಜು ಮಲ್ತೆರ್,<ref name="riddle"/> ಬೊಕ್ಕ ಎಂಜಿನಿಯರ್ ಡೇನಿಯಲ್ ಬಾಲಾಖೊವ್ಸ್ಕಿನ ಆಸ್ತಿ ಆಂಡ್,<ref name="mirtu"/> ಕೀವ್ ವ್ಯಾಪಾರಿನ ಮಗೆ,<ref name="KV"/> ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆದ ನಿರ್ದೇಶಕ ಮಂಡಳಿದ ಅಧ್ಯಕ್ಷೆರಾದಿತ್ತೆರ್,<ref name="KV"/> ಬೊಕ್ಕ ಕೀವ್ ಡ್ ಒರಿ ಫ್ರಾನ್ಸ್/ಫ್ರೆಂಚ್ ಕಾನ್ಸುಲಾರ್ ಏಜೆಂಟ್.<ref name="Fyodorovskaya">{{cite web|url=http://www.house-ua.com/news395.html |title=Hunter from the House with Chimaeras |access-date=2006-07-14 |first=Olga |last=Fyodorovskaya |work=Ukrainskiy Dom |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20070929175434/http://www.house-ua.com/news395.html |archive-date=September 29, 2007 }}</ref> ೧೯೧೬ಡ್, ಈ ಇಲ್ಲ್ ಬ್ಲಾಹೋಡಾಟಿನ್ಸ್ಕೋ ಸಕ್ಕರೆ ಕಾರ್ಖಾನೆಗ್ ಸೇರ್ದಿನವು.<ref name="riddle"/> ೧೯೧೮ಡ್, ಕಟ್ಟಡೊದ ಮಾಲೀಕತ್ವ ಮತ್ತೆ ಬದಲಾಂಡ್, ಸ್ಯಾಮ್ಯುಯೆಲ್ ನೆಮೆಟ್ಸ್ ಗ್.<ref name="riddle"/> ೧೯೨೧ಡ್, ಬೊಲ್ಶೆವಿಕ್ ಲು ಕೀವ್ ನ್ ನಿಯಂತ್ರಣ ಮಲ್ತಿನ ಬುಕ್ಕೊ, ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್/ಕೀವ್ ಮಿಲಿಟರಿ ಡಿಸ್ಟ್ರಿಕ್ಟ್ ದ ಕೆಲವು ಇಲಾಖೆಲು ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಸದನೊಡು ಕಚೇರಿಲೆನ್ ದೆತೊನುವೆರ್. ===ಒಡೆತನ ೧೯೨೧–೨೦೦೨=== ರಷ್ಯಾದ ಕ್ರಾಂತಿದ ನಂತರದ ಉಕ್ರೇನ್/ಅಶಾಂತಿದ ಅವಧಿ ರಷ್ಯಾದ ಕ್ರಾಂತಿ (೧೯೧೭) ನಂತರ, ಕಟ್ಟಡೊನು ರಾಷ್ಟ್ರೀಕರಣ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನನೊಂಜಿ ಸರ್ತಿ ಕಮ್ಯುನಲ್ಕಾ/ಸಾಮುದಾಯಿಕ ಜೀವನಗ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="president.gov.ua">{{cite web|url=http://www.president.gov.ua/en/slideshow/3.html |title=House with Chimeras |access-date=2008-03-20 |work=[[President of Ukraine|Official web-site of President of Ukraine]] |url-status=dead |archive-url=https://web.archive.org/web/20080425002336/http://www.president.gov.ua/en/slideshow/3.html |archive-date=2008-04-25 }}</ref> ಪ್ರತಿಯೊಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಸುಮಾರ್ ಒಂಜಿ ರ್ದ್ ಪತ್ತ್ ಕುಟುಮೊಲು ನೆಲೆಯಾದಿತ್ತೆರ್.<ref name="Horodetskyi"/> ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧ (೧೯೪೧-೧೯೪೩) ದ ಪೊರ್ತುಡು, ಈ ಕಟ್ಟಡೊನು ಬುಡ್ದು ಪೋಯೆರ್. ಯುದ್ಧದ ಸಮಯೊಡು ಕಠಿಣ ಅಂಶೊಲೆಗ್ ಒಡ್ಡಿಕೊಳ್ಳುನ ಕಾರಣ, ಕಟ್ಟಡೊಗು ಅಯಿತ ರಚನೆಗ್ ಮಸ್ತ್ ಹಾನಿ ಆಂಡ್.<ref name="Horodetskyi"/> ಯುದ್ಧೊದ ನಂತರ, ಈ ಕಟ್ಟಡೊನು ಇವಾನ್ ಫ್ರಾಂಕೊ ರಾಷ್ಟ್ರೀಯ ಶೈಕ್ಷಣಿಕ ನಾಟಕ ರಂಗಮಂದಿರಡ್ದ್ ಸ್ಥಳಾಂತರಿಸಾಯಿನ ನಟೆರೆಗ್ ನಿವಾಸವಾದ್ ಸಂಕ್ಷಿಪ್ತವಾದ್ ಬಳಕೆ ಮಲ್ತೆರ್;<ref name="KV"/> ಆಂಡಲಾ, ಉಕ್ರೇನಿಯನ್ ಸೋವಿಯೆತ್ ಸಮಾಜವಾದಿ ಗಣರಾಜ್ಯೊದ ಕಮ್ಯುನಿಸ್ಟ್ ಪಾರ್ಟಿ (ಬೊಲ್ಶೆವಿಕ್) ಆಫ್ ಉಕ್ರೇನ್ ದ ಕೇಂದ್ರ ಸಮಿತಿ ಈ ಕಟ್ಟಡೊದ ಒಡೆತನೊನು ದೆತೊನುಂಡು ಬೊಕ್ಕ ಅಯಿನ್ ಅಕ್ಲೆನ ಗಣ್ಯೆರೆಗ್ ಪಾಲಿಕ್ಲಿನಿಕ್ ನಂಬರ್ ೧ ಆದ್ ಪರಿವರ್ತಿಸಯೆರ್.<ref name="Horodetskyi"/> ಪಾಲಿಕ್ಲಿನಿಕ್ 20ನೆ ಶತಮಾನೊದ ಅಂತ್ಯೊಗು ಮುಟ್ಟ ಈ ಕಟ್ಟಡೊನು ಬಳಕೆ ಮಲ್ತ್ಂಡ್. ಆ ಸಮಯೊಡು, ಕಟ್ಟಡ ಬಹುತೇಕ ಅರ್ಧೊಗು ಪುಡಾಂಡ್. ಒಂಜಿ ಭಾಗ {{convert|22|cm|in|0|abbr=on}} ನ್ ಸಾಗ್ ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ ಒಂಜಿ ಪ್ರಮುಖ ಲಂಬ ಬಿರುಕು ರೂಪುಗೊಂಡುಂಡು, ಉಂದೆತ ಅಗಲ ಸುಮಾರ್ {{convert|40|cm|in|0|abbr=on}} ಉಂಡು.<ref name="Horodetskyi"/> ಕಟ್ಟಡೊದ ಕೆಲವು ವಾಸ್ತುಶಿಲ್ಪದ ವಿವರೊಲು ಒಂಜಿ ವೇಳೆ ಚಿಪ್ ಆತ್ಂಡ್, ಅತ್ತ್ಂಡ ಬಿರುಕು ಬುಲೆದ್ಂಡ್. ಕಟ್ಟಡೊದ ಜೀರ್ಣೋದ್ಧಾರದ ಬೇಲೆನ್ 2002 ಗ್ ನಿಗದಿ ಮಲ್ತೆರ್, ಆಂಡಲಾ ಪಾಲಿಕ್ಲಿನಿಕ್ ದ ಆಪರೇಟರ್ ಲು 40 ವರ್ಸೊಡ್ದ್ ಜಾಸ್ತಿ ಕಟ್ಟಡೊನು ಆಕ್ರಮಣ ಮಲ್ತ್ ದ್ ಬುಡುದು ಪೋಯೆರೆ ಹಿಂಜರಿಯೊಂದಿತ್ತೆರ್. ಕಟ್ಟಡೊಡ್ದ್ ನಿವಾಸಿಲೆನ್ ಪಿದಯಿ ಕನರೆ, ಕೆಲಸದಕುಲು ಮಾತಾ ಕಿಟಕಿಲೆನ್ ಮುಚ್ಚಿಯೆರ್ ಬೊಕ್ಕ ಪಾಲಿಕ್ಲಿನಿಕ್ ಆವರಣೊನು ಖಾಲಿ ಮಲ್ತ್ಜಿಂಡ ಬಾಗಿಲುಲೆನ್ ಲಾ ಅಂಚನೆ ಮಲ್ಪುವೆ ಪಂದ್ ಬೆದರಿಕೆ ಮಲ್ತೆರ್. ಈ ವಿಷಯೊಡು ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ನ ಪಾಲ್ ಪಡೆಯಿನೆಡ್ದಾವರ ಮಾತ್ರ ಪಾಲಿಕ್ಲಿನಿಕ್ ನ್ ಸಂಪೂರ್ಣವಾದ್ ಪಿದಯಿ ಬುಡ್ಪಾಯೆರ್.<ref name="mirtu"/> ===ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಅಧಿಕೃತ ಬಳಕೆ=== [[File:Flags of EU and Ukraine.jpg|thumb|ಒಂಜಿ ಅಧಿಕೃತ ಕಾರ್ಯಕ್ರಮೊದ ಪೊರ್ತುಡು ಕಟ್ಟಡೊದ ಎದುರು ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪಿಯನ್ ಒಕ್ಕೂಟದ ಧ್ವಜೊಲು]] ಜೀರ್ಣೋದ್ಧಾರದ ಸಮಯೊಡು, ''ಪ್ರಾಜೆಕ್ಟ್ ರೆಸ್ಟಾವ್ರಾಟ್ಸಿಯಾ'' ನಡಪಾಯಿನ ಬೊಕ್ಕ ನಟಾಲಿಯಾ ಕೊಸೆಂಕೊ ಮುಖ್ಯಸ್ಥೆರಾದ್,<ref name="vr-829-р">{{Cite Ukrainian law |type=[[Cabinet of Ministers of Ukraine|Cabinet of Ministers decree]]|number=829-р|law=829-2003-р |name=On the recognition of the project and the company carrying out the building's reconstruction as well as the restoration of the monument of architecture and the government monument of the city on Bankova St., 10 in the city of Kyiv|date=2003-12-31}}</ref> ಕಟ್ಟಡದ ಅಡಿಪಾಯೊನು ಬಲಪಡಾಯೆರೆ ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟ ಕಾಲೊಡು ತುಂಬಿನ ಇಡೀ ಕೆಳಮಹಡಿನ್ ಕೆಲಸದಕುಲು ಕಂಡು ಕನತೆರ್.<ref name="mirtu"/> ಒಳಾಂಗಣದ ವಿಸ್ತಾರವಾದ್ ಅಲಂಕಾರೊನು ಪೂರ್ತಿ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪೊಡಾಂಡ್. ಅಂಗಳೊಡು, ಪುನಃಸ್ಥಾಪಕೆರ್ ಒಂಜಿ ಕೃತಕ ಕೆರೆ, ಕಾರಂಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಎಲ್ಯ ಉದ್ಯಾನೊನು ದೀಯೆರ್—ಉಂದು ಮಾತ ಹೊರೊಡೆಕಿನ ಮೂಲ ಯೋಜನೆಲೆಡ್ ಇತ್ತ್ಂಡ್.<ref name="mirtu"/> ಈ ಕಟ್ಟಡೊನು ನವೆಂಬರ್ ೨೦೦೪ಡ್ ರಾಷ್ಟ್ರೀಯ ಕಲಾ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ "ಉಕ್ರೇನಿಯನ್ ಕಲೆತ ಮೇರುಕೃತಿಲು" ಆದ್ ತೆರೆಯೆರ್.<ref name="museum">{{cite news|url=http://www.korespondent.net/main/31543 |title=A cultural museum center opens in the House with Chimaeras |access-date=2007-07-12 |date=2004-11-19 |work=[[Korrespondent]] |publisher=Bigmir-Internet |language=uk |archive-url=https://web.archive.org/web/20070927234156/http://www.korespondent.net/main/31543 |archive-date=September 27, 2007 |url-status=dead }}</ref> ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಆದ್ ಬೊಕ್ಕ ರಾಜ್ಯೊದ ಸಂದರ್ಶಕೆರೆಗ್ ಅಧ್ಯಕ್ಷೀಯ ಸಭೆತ ಜಾಗೆ ಆದ್ ದ್ವಿಗುಣ ಉದ್ದೇಶೊಗು ಸೇವೆ ಮಲ್ಪುಂಡು ಪನ್ಪಿನ ನಿರೀಕ್ಷೆ ಇತ್ತ್ಂಡ್. ಏಪ್ರಿಲ್ ೨೦೦೫ಡ್, ಕೀವ್ ಸಿಟಿ ಕೌನ್ಸಿಲ್ ಒಂಜಿ ಮಸೂದೆನ್ ಸಲ್ಲಿಸಯೆರ್<ref>[http://www.kmr.gov.ua/decree.asp Decrees of Kyiv City Council] {{webarchive|url=https://web.archive.org/web/20070707053720/http://kmr.gov.ua/decree.asp |date=2007-07-07 }} [[Kyiv City Council|Kyiv City Council decree]] No. '''380/2955''': [http://www.kmr.gov.ua/decree.asp?Id=2235 On the allowance of the change of ownership to the government authority with permission of conducting reconstruction and restoration works on the building of 10, Bankova Street] {{webarchive|url=https://web.archive.org/web/20070927225321/http://www.kmr.gov.ua/decree.asp?Id=2235 |date=2007-09-27 }}. Passed on 2005-04-21. {{in lang|uk}}</ref> [[ಉಕ್ರೇನಿಯನ್ ಹೈವ್ನಿಯಾ|₴]]104 ಮಿಲಿಯನ್ (ಅಂದಾಜಿ ಯುನೈಟೆಡ್ ಸ್ಟೇಟ್ಸ್ ಡಾಲರ್/ 20 ಮಿಲಿಯನ್) ಗ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರಗ್ ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮನೆತ ಪುನರ್ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನಃಸ್ಥಾಪನೆಗ್.<ref name="proUA">{{cite news|url=http://ua.proua.com/news/2005/05/11/180559.html|archive-url=https://web.archive.org/web/20070930044045/http://ua.proua.com/news/2005/05/11/180559.html|url-status=dead|archive-date=2007-09-30|title="House with Chimaeras" received the status of an official governmental residence|access-date=2006-09-30|date=2004-11-19|work=proUA|language=uk}}</ref> ಅಧಿಕೃತ ಸಮಾರಂಭೊಲೆಡ್ ಬಳಕೆ ಮಲ್ಪುನ ಕಟ್ಟಡೊದ ಎದುರುಡು ಒಂಜಿ ಪೊಸ ಚೌಕೊನು (ಎಲ್ಲಾ ಆಟೋಮೊಬೈಲ್ ಟ್ರಾಫಿಕ್ ನ್ ಮುಚ್ಚುನ) ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆಲಾ ಕೌನ್ಸಿಲ್ ಉಕ್ರೇನಿಯನ್ ಸರ್ಕಾರೊಗು ಅನುಮತಿ ಕೊರ್ತುಂಡು.<ref name="proUA"/> ಮೇ ೨೦೦೫ಡ್ದ್, ಈ ಕಟ್ಟಡೊ ಒಂಜಿ ಅಧಿಕೃತ ಉಕ್ರೇನ್ ದ ಅಧ್ಯಕ್ಷೆರ್ನ| ನಿವಾಸ ಆದುಂಡು, ಅಧಿಕೃತ ಬೊಕ್ಕ ರಾಜತಾಂತ್ರಿಕ ಸಮಾರಂಭೊಲೆಗ್ ಉಪಯೋಗ ಆಪುಂಡು.<ref name="vr-340"/><ref>{{Cite web |title=Ukrainian architecture: 10 buildings that everyone should see |url=https://rubryka.com/en/article/architecture-of-ukraine/}}</ref><ref name="BBC Ukrainian"/> ದಿ ಹೌಸ್ ವಿತ್ ಚಿಮೇರಾಸ್ ನ್ ಉಕ್ರೇನಿಯನ್ ಅಧ್ಯಕ್ಷೆರ್ ವಿಕ್ಟರ್ ಯುಶ್ಚೆಂಕೊ ಬೊಕ್ಕ ರಷ್ಯಾದ ಅಧ್ಯಕ್ಷೆರ್ ವ್ಲಾಡಿಮಿರ್ ಪುಟಿನ್ ನಡುಟು ಒಂಜಿ ಸಭೆತ ಜಾಗೆ ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್, ಅಪಗ ಚಿಮೇರಾಸ್ ಡಿಸೆಂಬರ್ ೨೨, ೨೦೦೬ಡ್ ಕೀವ್ ಗ್ ಭೇಟಿ ಕೊರ್ತೆರ್.<ref>{{cite news|url=http://www.korrespondent.net/main/174172 |title=Yushchenko and Putin decided their goals for two years ahead|access-date=2007-07-12|date=2006-12-22|work=[[Korrespondent]]|publisher=Bigmir-Internet|language=ru}}</ref> ಕಟ್ಟಡೊಡು ಮಾತುಕತೆ, ಪಾತೆರ, ಅಧಿಕೃತ ದಾಖಲೆಲೆಗ್ ಸಹಿ ಪಾಡುನ ಕೊಠಡಿಲು, ಅಂಚನೆ ಪತ್ರಿಕೆಲೆಗ್ ವಿಶೇಷ ಕೊಠಡಿ ಸೇರ್ದ್ ಉಂಡು.<ref name="president.gov.ua"/> ==ವಾಸ್ತುಶಿಲ್ಪ== [[File:Architectural details on House with Chimaeras 2007.JPG|thumb|250px|ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ವಿನ್ಯಾಸಗೊಳಿಸಾಯಿನ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಉಭಯಚರೊಲು ಬೊಕ್ಕ ಬೇತೆ ಜೀವಿಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರತಿಮೆಲೆನ ವಿವರವಾದ್ ನೋಟ]] {| cellpadding="1" style="float:left; border:1px solid #8888aa; padding:5px; font-size:95%; margin:0 15px 0 0; background:#f9f9f9; width:200px;" |- | architectural_style=" text-align: center;" |ವೊಲೊಡಿಮಿರ್ ಯಾಸಿಯೆವಿಚ್ <ref name="wood">{{harvnb|Yasiievych|1988|p=93.}}</ref> |- |<small>ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಇಲ್ಲ್ ನ್ ಆಂಟೋನಿ ಗೌಡಿ ನ ಕೆಲಸೊಲೆನೊಟ್ಟುಗು ಮಾತ್ರ ಹೋಲಿಕೆ ಮಲ್ಪೊಲಿ, ವಿಶೇಷವಾದ್ ಬಾರ್ಸಿಲೋನಾ, ಸ್ಪೇನ್ ಡ್ ಪ್ರಸಿದ್ಧ ಕಾಸಾ ಮಿಲಾ, ಒಂಜಿ ರಡ್ಡ್ ವರ್ಷೊಡ್ದು ಬೊಕ್ಕ (೧೯೦೫-೧೯೧೦) ನಿರ್ಮಾಣ ಆಯಿನ ನಡುಟು.</small> |} ಈ ಕಟ್ಟಡೊನು ಆರ್ಟ್ ನೌವಿಯೊ ಶೈಲಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಉಂದು ಆ ಕಾಲೊಡು ತುಲನಾತ್ಮಕವಾದ್ ಪೊಸ ಶೈಲಿ ಆದಿತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಹರಿಯುನ, ವಕ್ರರೇಖೆದ ವಿನ್ಯಾಸೊಲೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್, ಉಂದೆಟ್ ಮಸ್ತ್ ಸರ್ತಿ ಪುಷ್ಪ ಬೊಕ್ಕ ಬೇತೆ ಸಸ್ಯ-ಪ್ರೇರಿತ ಮೋಟಿಫ್ ಲೆನ್ ಸೇರಾವೊಂದಿತ್ತೆರ್. ಹೊರೊಡೆಕಿ ಕಟ್ಟಡದ ಪಿದಯಿದ ಅಲಂಕಾರೊಡು ಪೌರಾಣಿಕ ಜೀವಿಲೆನ ಬೊಕ್ಕ ಮಲ್ಲ ಆಟದ ಪ್ರಾಣಿಲೆನ ರೂಪೊಡು ಇಂಚಿನ ಮೋಟಿಫ್ ಲೆನ್ ತೋಜಾದ್ ಕೊರ್ತೆರ್. ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಮಲ್ತಿನ ಇಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಬೇಲೆಡ್ದ್ ಕೀವ್ ದ ಆಂಟೋನಿ ಗೌಡಿ ಪನ್ಪಿನ ಅಡ್ಡ ಪುದರ್ ನ್ ಗಳಸೊಂಡೆರ್.<ref name="mirtu"/><ref name="element"/> ಕಟ್ಟಡ ಉಪ್ಪುನ ಕಡಿಮೆ ಇಳಿಜಾರುಡ್ದ್, ಅಯಿತ ಅಡಿಪಾಯೊಗು ಸರಿಯಾದ್ ಸರಿ ಆಯೆರೆ ಕಾಂಕ್ರೀಟ್ ಡ್ದ್ ವಿಶೇಷವಾದ್ ವಿನ್ಯಾಸಗೊಳಿಸೊಡಾಪುಂಡು. ಎದುರುಡ್ದ್ ತೂನಗ, ಕಟ್ಟಡೊಗು ಮೂಜಿ ಮಹಡಿಲು ಮಾತ್ರ ಉಪ್ಪುನಂಚ ತೋಜುಂಡು. ಆಂಡಲಾ, ಪಿರವುಡ್ದ್, ಅಯಿತ ಆಜಿ ಮಹಡಿಲೆನ್ ತೂವೊಲಿ.<ref name="vlasenko">{{harvnb|Zharikov|1983–1986|p=38.}}</ref>ಕಟ್ಟಡದ ಅಡಿಪಾಯೊದ ಒಂಜಿ ಭಾಗೊನು ಕಾಂಕ್ರೀಟ್ ರಾಶಿಲೆಡ್ ಮಲ್ತೆರ್, ಬೊಕ್ಕೊಂಜಿ ಭಾಗೊನು ನಿರಂತರ ಅಡಿಪಾಯೊದ ರೂಪೊಡು ಮಲ್ತೆರ್. ಸಾಮಾನ್ಯವಾದ್, ಈ ರಡ್ಡ್ ವಿಧಾನೊಲು ಎಡ್ಡೆ ರೀತಿಡ್ ಮಿಶ್ರಣ ಆಪುಜಿ ಆಂಡ ಹೊರೊಡೆಕಿ ಒಂಜಿ ರೀತಿಡ್ ಈ ತಾಂತ್ರಿಕ ಸಮಸ್ಯೆನ್ ಜಯಿಪುನಗ ಯಶಸ್ವಿಯಾಯೆರ್. ಇಟಾಲಿಯನ್ ಶಿಲ್ಪಿ ಎಮಿಲಿಯೊ ಸಾಲಾ (ಶಿಲ್ಪಿ) ಆಂತರಿಕ ಬೊಕ್ಕ ಬಾಹ್ಯ ಶಿಲ್ಪಕಲೆತ ಅಲಂಕಾರೊಲೆಗ್ ಜವಾಬ್ದಾರರಾದಿತ್ತೆರ್, ಉದಾಹರಣೆಗೆ ಮತ್ಸ್ಯಕನ್ಯೆಲು, ಡಾಲ್ಫಿನ್ ಲು, ಬೊಕ್ಕ ಕಟ್ಟಡದ ಛಾವಣಿಡ್ ಕಪ್ಪೆಲು, ಪಿದಯಿದ ಗೋಡೆಲೆಡ್ ಮುಳುಗಡೆ ಆಪಿನ ನೌಕೆಲು ಬೊಕ್ಕ ಬೇಟೆದ ಟ್ರೋಫಿಲು, ಬೊಕ್ಕ ಒಳಾಂಗಣ ಅಲಂಕಾರೊಲು, ಉದಾಹರಣೆಗೆ ಮೆಟ್ಟಿಲುಲು ಬೊಕ್ಕ ಮಲ್ಲ ವೇಲು ಚಾಂಡೆಲಿಯರ್ಲು ನಿಂಫೆಯಾ/ಕಮಲ ಪುಷ್ಪೊಲೆನ ಕಾಂಡೊಲೆಡ್ ಕಚ್ಚಿದ್ ದೀತಿನ ಮಲ್ಲ ಕ್ಯಾಟ್ ಫಿಶ್ನ್ ಚಿತ್ರಿಸವುಂಡು. ಸಾಲಾ ಮಲ್ತಿನ ಪಿದಯಿದ ಶಿಲ್ಪೊಲು [[ಸಿಮೆಂಟ್]] ಡ್ದ್ ಮಲ್ಪುನವು. ಸಿಮೆಂಟ್ ದ ಉತ್ಪಾದನೆ «ಫಾರ್» ಕಂಪೆನಿಡ್ ಇತ್ತ್ಂಡ್, ಅಯಿಟ್ ಹೊರೊಡೆಕಿ ಸಹ-ನಿರ್ದೇಶಕೆರ್ ಆದಿತ್ತೆರ್.<ref name="klymenko">{{cite web|url=http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|archive-url=https://web.archive.org/web/20050310062635/http://klymenko.data-tec.net/Kyiv/Kyiv.Pechersk.htm|url-status=dead|archive-date=2005-03-10|title=Budynok z Khimeramy|access-date=2006-09-30|first=Sergiy|last=Klymenko|work=Photos of Kyiv|language=uk, ru}}</ref> ಕಂಪೆನಿದ ಮುಖ್ಯ ನಿರ್ದೇಶಕೆರ್ ರಿಚ್ಟರ್ ನ ಕೋರಿಕೆದ ಪ್ರಕಾರ ಸಿಮೆಂಟ್ ನ್ ಪ್ರಾಥಮಿಕ ಕಟ್ಟಡ ಸಾಮಗ್ರಿ ಆದ್ ಮಾತ್ರ ಬಳಕೆ ಮಲ್ತೆರ್.<ref>{{cite web|url=http://www.narodnapravda.com.ua/travel/468d12ca51334/|title=Walks around Kyiv. House with Chimaeras|access-date=2006-07-14|author=explorer|date=2007-07-05|work=Narodna pravda|language=uk|archive-date=2011-07-25|archive-url=https://web.archive.org/web/20110725230507/http://www.narodnapravda.com.ua/travel/468d12ca51334/|url-status=dead}}</ref>ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣದ ಸಮಯೊಡು, ಸಿಮೆಂಟ್ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿ ಆದ್ ಜನಪ್ರಿಯ ಆದಿಜ್ಜಿ, ಅಂಚಾದ್ ಅಯಿತ ಬಳಕೆನ್ ಇಲ್ಲ್ ಬೊಕ್ಕ ಕಟ್ಟಡೊದ ಸಾಮಗ್ರಿಲೆಗ್ ಪ್ರಚಾರೊದ ರೂಪೊಡು ಬಳಕೆ ಮಲ್ತೆರ್.<ref name="klymenko"/> <gallery class="center" widths=250 heights=185> File:Architectural details on House with Chimaeras 2007-2.JPG|ಜಿಂಕೆ ಬೊಕ್ಕ ಖಡ್ಗಮೃಗಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ವಿಸ್ತಾರವಾದ್ ಸಿಮೆಂಟ್ ಅಲಂಕಾರೊಲು File:Elephant on House with Chimaeras.JPG|ಈ ಕಟ್ಟಡೊಡು ಬೇತೆ ಬೇತೆ ಕೆತ್ತನೆಲು ಉಂಡು, ಉದಾಹರಣೆಗೆ ಈ ಆನೆ-ತಲೆತ ಗಾರ್ಗೊಯ್ಲ್ File:Architectural details on House with Chimaeras 2007-3.JPG|ಕಟ್ಟಡೊದ ಸುತ್ತ ತೂಯಿನ ಮಸ್ತ್ ಮೂರ್ತಿಲೆಡ್ ಒಂಜಿ </gallery> ===ನೆಲತ ಯೋಜನೆ=== [[File:House with Chimaeras (Plan).JPG|thumb|ಆಜನೇ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅರೆನ ಸ್ವಂತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಹೊರೊಡೆಕಿನ ಮೂಲ ನೀಲನಕ್ಷೆಲು; ೧೯೦೦ ದಶಕದ ಸುರುಟು]] ಚಿಮೇರಾಸ್ ನ ಇಲ್ಲ್ ನ್ ಬಾಡಿಗೆದಾರೆರ್ ಇಡೀ ಮಹಡಿನ್ ಆಕ್ರಮಣ ಮಲ್ಪುನಂಚಿನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್, ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿಡ್ ಖಾಸಗಿ ಅಡುಗೆಮನೆಡ್ದ್ ಎಲ್ಯ ಪುಡಿ ಕೊಠಡಿಲೆಗ್ ಮುಟ್ಟ ಅಗತ್ಯವಾಯಿನ ಮಾತಾ ಇಲ್ಲದ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಕಟ್ಟಡೊದ ಉಲಯಿ ಉಪ್ಪುನ ತೆರೆದ ನೆಲತ ಯೋಜನೆ ಬೊಕ್ಕ ಹೆಚ್ಚುವರಿ ಕೊಠಡಿಲು 20ನೆ ಶತಮಾನೊದ ಸುರುತ ಶ್ರೀಮಂತೆರೆನ ಇಲ್ಲ್ ಲೆನ ಲಕ್ಷಣೊಲು.<ref name="vlasenko"/> ಒಟ್ಟುಗು, ಕಟ್ಟಡೊದ ವಿಸ್ತೀರ್ಣ {{convert|3309.5|m2|sqft|2|abbr=on}} ಉಂಡು.<ref name="vr-829-р"/> ಗುಡ್ಡೆದ ಆಳೊಡು ಇಪ್ಪುನ ಕಟ್ಟಡೊದ ಕೆಳಮಟ್ಟೊಡು ರಡ್ಡ್ ಸ್ಟೆಬಲ್ಲು, ಕೋಚ್ ಮ್ಯಾನ್ಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಪಾಲ್ ಪಡೆಯಿನ ಬಟ್ಟೆ ಒಯ್ಪುನ ಜಾಗೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಪ್ರತ್ಯೇಕ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್. ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಸ್ಟೋರೇಜ್ ರೂಮ್ ಇತ್ತ್ಂಡ್. ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆಡ್ ಸುರುತ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ರಡ್ಡ್ ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್, ಬೊಕ್ಕ ರಡ್ಡನೆಡ್ ಮೂಜಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್.{{refn|೧೯೦೩ಡ್ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ ೫೪೦ ಬೊಕ್ಕ ೪೨೦ ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್, ಅಯಿಕ್ ಅನುಗುಣವಾದ್.{{sfn|Malakov|1999}}|group=nb}} ಕೆಳಮಟ್ಟೊದ ಮಿತ್ತ್ ದ ಪ್ರತಿಯೊಂಜಿ ಮಹಡಿನ್ ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಮಾತ್ರ ದೀಯೆರೆ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ರಡ್ಡನೆ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಫೋಯರ್, ಅಡುಗೆಮನೆ, ಬಫೆಟ್, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲೆನ್ ಒಟ್ಟು ಮಲ್ತ್ ದ್ ಆರು ವಸತಿ ಕೊಠಡಿಲು ಇತ್ತ್ಂಡ್. ಒಂಜೇ ಮಟ್ಟೊಡು ನಾಲ್ ವೈನ್ ಸೆಲ್ಲರ್ ಲಾ ಇತ್ತ್ಂಡ್.{{refn|Tಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ದ ಬೆಲೆ 1903ಡ್ 1,200 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ನೆಲಮಾಳಿಗೆಲು ಮಿತ್ತ್ ದ ಮಟ್ಟೊಡು ಉಪ್ಪುನ ಅಪಾರ್ಟ್ ಮೆಂಟ್ ಲೆಗ್ ಸೇರ್ದಿನವು. ಮೂಜಿನೆ ಮಹಡಿಡ್, ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಎಂಟು ವಸತಿ ಕೊಠಡಿಲು, ಒಂಜಿ ಫೋಯರ್, ಒಂಜಿ ಅಡುಗೆಮನೆ, ಸ್ಕಲ್ಲರಿ (ಕೋಣೆ)/ಪಾತ್ರೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಸೇವಕೆರೆಗ್ ರಡ್ಡ್ ಕೊಠಡಿಲು, ಒಂಜಿ ಸ್ನಾನದ ಕೋಣೆ, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು ಇತ್ತ್ಂಡ್.{{refn|ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸುರುತ ವಾರ್ಷಿಕ ಬಾಡಿಗೆ 2,000 ರೂಬಲ್ಸ್ ಆದಿತ್ತ್ಂಡ್.{{sfn|Malakov|1999}}|group=nb}} ಈ ಅಪಾರ್ಟ್ಮೆಂಟ್ ನ್ ಬಂಕೋವಾ ಸ್ಟ್ರೀಟ್ ದ ಮಟ್ಟೊಡ್ದ್ ಸ್ವಲ್ಪ ಕೆಳಗ್ ದೀಯೆರ್, ಎದುರುದ ಪ್ರವೇಶ ದ್ವಾರೊಡ್ದ್. ಹೊರೊಡೆಕಿಗ್ ಸೇರ್ದಿನ ಮಲ್ಲ ಅಪಾರ್ಟ್ಮೆಂಟ್, ಒಂಜಿ ಅಧ್ಯಯನ (ಕೋಣೆ), ಒಂಜಿ ಮಲ್ಲ ಕೋಣೆ ಬೊಕ್ಕ ಒಂಜಿ ಲಿವಿಂಗ್ ರೂಮ್, ಒಂಜಿ ಡೈನಿಂಗ್ ರೂಮ್, ಒಂಜಿ ಬೌಡೋಯಿರ್, ಒಂಜಿ ಬೆಡ್ ರೂಮ್, ಒಂಜಿ ಜೋಕುಲೆನ ಕೋಣೆ, ಒಂಜಿ ಗವರ್ನೆಸ್ ಗ್ ಒಂಜಿ ಕೋಣೆ, ಒಂಜಿ ಅತಿಥಿ ಕೋಣೆ, ಮೂಜಿ ಸೇವಕೆರೆನ ಕೋಣೆ, ಅಡುಗೆಮನೆ ತೊಳೆಯೆರೆ ಕೊಠಡಿ, ಒಂಜಿ ಕೋಣೆನ್ ಒಳಗೊಂಡಿತ್ತುಂಡ್ ಸ್ನಾನದ ಕೋಣೆ, ರಡ್ಡ್ ಶೌಚಾಲಯೊಲು, ಬೊಕ್ಕ ರಡ್ಡ್ ಶೇಖರಣಾ ಕೊಠಡಿಲು.<ref name="Horodetskyi"/> ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಹೊರೊಡೆಕಿನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಗ್ ಸಮಾನವಾದ್ ಗಾತ್ರ ಬೊಕ್ಕ ವಿನ್ಯಾಸೊಡು ಒಂಜಿ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಇತ್ತ್ಂಡ್.{{refn|ಈ ರಡ್ಡ್ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲೆನ್ 1903ಡ್ ವಾರ್ಷಿಕ 3,500 ರೂಬಲ್ಸ್ ಗ್ ಬಾಡಿಗೆಗ್ ಕೊರ್ತೆರ್.{{sfn|Malakov|1999}}|group=nb}} ಮಿತ್ತ್ ದ ಮಹಡಿಡ್ ಉಪ್ಪುನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಡ್ ಒಂಜಿ ಕೋಣೆ ಕಮ್ಮಿ ಇತ್ತ್ಂಡ್; ಇಂದೆನ್ ಸರಿ ಮಲ್ಪರೆ, ನಗರೊದ ವಿಹಂಗಮ ದೃಶ್ಯೊನು ಒದಗಾವುನ ಒಂಜಿ ಸಂಪರ್ಕ ಟೆರೇಸ್ ಇತ್ತ್ಂಡ್.<ref name="Horodetskyi"/>{{refn|The rental cost for this dwelling was 2,750 rubles annually. For comparison, an average salary for a librarian was about 50 rubles per year.{{sfn|Ivanenko|2004}}|group=nb}} ==ದಂತಕಥೆಲು== [[File:House with Chimaeras gardens.JPG|thumb|ಕಟ್ಟಡೊಗು ಪಕ್ಕೊಡು ಉಪ್ಪುನ ಉದ್ಯಾನೊಲು ಅಧ್ಯಕ್ಷೀಯ ಆಡಳಿತ ಕಟ್ಟಡ (ಕೀವ್) ಹಿನ್ನೆಲೆಡ್ ತೋಜುಂಡು]] ವರ್ಷೊಡ್ದಿಂಚಿ, ಚಿಮೇರಾಸ್ ನೊಟ್ಟುಗು ಇಪ್ಪುನ ಇಲ್ಲ್ ದ ಅಸಾಮಾನ್ಯ ಸ್ವಭಾವೊ ಮಾರ್ಗದರ್ಶಿ-ಪುಸ್ತಕೊಲೆಡ್ ಅತ್ತಂದೆ ಪತ್ರಿಕೆಲೆಡ್ ಕೆಲವೊಂಜಿ ಸರ್ತಿ ಪುನರಾವರ್ತಿತವಾದ್ ಬರ್ಪಿನ ಒಂಜಿ ಸಂಖ್ಯೆದ ಕತೆಕುಲೆಗ್ ಕಾರಣವಾತ್ಂಡ್, ಆಂಡಲಾ ಅವು ಅಸತ್ಯ ಅತ್ತಂದೆ ಯಾವುದೇ ಪರಿಶೀಲಿಸಬಹುದಾದ ಮೂಲೊನು ಕಳೆವೊಂದಿಜ್ಜಿ.<ref>{{harvnb|Savchuk|1996|p=95.}}</ref> ಸುರುತ ದಂತಕಥೆದ ಪ್ರಕಾರ, ವ್ಲಾಡಿಸ್ಲಾವ್ ಹೊರೊಡೆಕಿನ ಮಗೆ ಕೆಲವು ದುರದೃಷ್ಟಕರ ಪ್ರೇಮ ಸಂಬಂಧೊಡ್ದ್ ಅತ್ತಂಡ ಕುಟುಂಬದ ಜಗಳೊಡ್ದ್ ಡ್ನಿಪರ್ ನದಿಗ್ ಜಂಪ್ ಮಲ್ತ್ ದ್ ಆತ್ಮಹತ್ಯೆ ಮಲ್ತೆರ್.{{refn|It was actually previous owner's ([[Friedrich Mering|Professor Mering]]) daughter who drowned.{{sfn|Ivanenko|2004}}|group=nb}}ಫಲವಾದ್, ಹೊರೊಡೆಕಿ ಸ್ವಲ್ಪ ಪಿಚ್ಚರ್ ಆದ್ ತನ್ನ ಮಗೆನ ನೆಂಪುಗು ಈ ಕತ್ತಲೆದ ಇಲ್ಲ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="kievinfo">{{cite web|url=http://www.kiev.info/culture/house_chimeras.htm|title=House with Chimaeras|access-date=2006-02-16|first=Viktor|last=Pavlovsky |work=Kiev Info|publisher=Optima Tours}}</ref> ರಡ್ಡನೆ ದಂತಕಥೆ ಪಂಡ ಹೊರೊಡೆಕಿ ಕೆಲವು ಬೇತೆ ವಾಸ್ತುಶಿಲ್ಪಿಲೆನೊಟ್ಟುಗು ಒಂಜಿ ಪಂತೊನು ಮಲ್ತೆರ್, ವಾಸ್ತುಶಿಲ್ಪಿ ಅಲೆಕ್ಸಾಂಡರ್ ಸ್ಕೋಬೆಲೆವ್ ಸೇರ್ದ್, ಅಂಚನೆ ಭೂಪ್ರದೇಶೊಡು ಒಂಜಿ ಇಲ್ಲ್ ಕಟ್ಟೆರೆ ಅಸಾಧ್ಯ ಪನ್ಪಿನೆನ್ ಸಾಬೀತು ಮಲ್ಪೆರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್, ದಾಯೆ ಪಂಡ ಆ ಜಾಗೆ (ಇವಾನ್ ಫ್ರಾಂಕೊ ಥಿಯೇಟರ್ ದ ಸಮೀಪೊಡು) ಒಂಜಿ ಜವುಗು (''ಕೊಜ್'ಯೆ ಬೊಲೊಟೊ'') ಮಿತ್ತ್ ಉಂಡು. ಕೀವ್ ದ ನಿರ್ಮಾಣ ಸಮಿತಿ ಈ ನಿರ್ದಿಷ್ಟ ಲಾಟ್ ಡ್ ಏರ್ಲಾ ರಚನೆಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ನಿಷೇಧ ಮಲ್ತ್ಂಡ್, ಆಂಡ ಅಂಚನೆ ಕಟ್ಟಡೊದ ನಿರ್ಮಾಣೊಡು ಹೊರೊಡೆಕಿ ಪಂತೊನು ಗೆಂದಿಯೆರೆ ಅನುವು ಮಲ್ತೆರ್.<ref name="kievinfo"/> ಮೂಜಿನೆ ದಂತಕಥೆದ ಪ್ರಕಾರ, ಹೊರೊಡೆಕಿ 1913ಡ್ ಶಾಪ ಕೊರ್ತೆರ್ (ಅರೆನ ಫೋರ್ಕ್ಲೋಸರ್/ಅರೆನ ಸಾಲಗಾರೆರೆಗ್ ಮರುಪಾವತಿ ಮಲ್ಪೆರೆ ಅಸಮರ್ಥತೆ ಕಾರಣ); ಇಲ್ಲ್ ದ ಮಾತಾ ಬಾಡಿಗೆದಾರೆರ್ ದುಖಿಡ್ ಉಪ್ಪುವೆರ್ ಅತ್ತ್ಂಡ ಒಂಜಿ ರೀತಿದ ಆರ್ಥಿಕ ದುರದೃಷ್ಟೊನು ಎದುರಿಸವೆರ್. ಕಟ್ಟಡೊದ ಒಂಜಿ ಭಾಗೊನು ಬಾಡಿಗೆಗ್ ದೆತೊನುನ ಮಾತಾ ವ್ಯವಹಾರೊಲು ಒಂಜಿ ವೇಳೆ ದಿವಾಳಿ ಪೋಯೆರ್, ಅಕ್ಲೆನ ನಿಧಿನ್ ಕಳವು ಮಲ್ತೆರ್ ಅತ್ತ್ಂಡ ರದ್ದು ಮಲ್ತೆರ್ ಪನ್ಪಿನ ಒಂಜಿ ಕತೆ ಉಂಡು.<ref name="mirtu"/> == ಉಲ್ಲೇಕೊಲು == === ಟಿಪ್ಪಣಿಲು === {{Reflist|group="nb"|30em}} === ಅಡಿಟಿಪ್ಪಣಿಲು === {{Reflist|30em}} === ಗ್ರಂಥಸೂಚಿ === {{Refbegin}} * {{Cite journal|last=Ivanenko|first=Anna|title=Horodecki. Lord of Chimaeras|url=http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|volume=1–2|journal=Mir Turizma|year=2004|location=[[Kyiv]]|issn=1811-3583|language=ru|url-status=dead|archive-url=https://web.archive.org/web/20070506082208/http://www.mirtu.com/archiv/MT-01-02-2004/articles/ART8.htm|archive-date=2007-05-06}} * {{Cite book|last=Malakov|first=Dmytro|title=Arkhitektor Horodetskyi|publisher=Kyi|year=1999|location=[[Kyiv]]|isbn=966-7161-30-7|language=uk}} * {{Cite book|last=Malikenaite|first=Ruta|title=Touring Kyiv|publisher=Baltija Dryk|year=2003|isbn=966-96041-3-3|language=uk}} * {{Cite book|last=Savchuk|first=Galina|title=The Streets of Kiev: Five Walks in the Centre|publisher=ArtEc Publishers|year=1996|location=[[Kyiv]]}} * {{Cite book|last=Yasiievych|first=V. E.|title=Architecture of Ukraine at the Turn of the 19th–20th Centuries|publisher=Budivel'nyk|year=1988|location=[[Kyiv]]|isbn=5-7705-0088-3|oclc=19693625|language=ru}} * {{Cite book|last=Zharikov|first=N. L.|title=Monuments of Urban Development and Architecture in the Ukrainian SSR|chapter=Residential house (Horodecki's), 1904|chapter-url=http://ua.vlasenko.net/_pgs/pgs-html/pgs1-038.html|volume=1–4|publisher=Budivel'nyk|year=1983–1986|location=[[Kyiv]]|language=ru|lccn=84179019}} {{Refend}} ==ಬಾಹ್ಯ ಕೊಂಡಿಲು== {{Commons category|House with Chimeras (Kyiv)}} * {{Cite web|url=http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|title=Art Nouveau in Kyiv|first=Dmytro|last=Horbachov|access-date=2006-09-30|archive-url=https://web.archive.org/web/20041104061652/http://www.wumag.kiev.ua/wumag_old/archiv/2_98/art.htm|archive-date=2004-11-04|url-status=dead|ref=none}} * {{Cite encyclopedia|url=http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|title=House with Chimaeras|encyclopedia=Wiki-Encyclopedia Kyiv|language=uk|access-date=2011-06-21|archive-date=2022-03-07|archive-url=https://web.archive.org/web/20220307105847/http://wek.kiev.ua/uk/%D0%91%D1%83%D0%B4%D0%B8%D0%BD%D0%BE%D0%BA_%D0%B7_%D0%A5%D1%96%D0%BC%D0%B5%D1%80%D0%B0%D0%BC%D0%B8|url-status=dead}} * {{Cite web|url=http://www.interesniy.kiev.ua/old/architecture/buildings/2 |script-title=ru:Замки Киева: Легенды и были Замка Ричарда, Дом с Химерами, замок барона Штейнгеля|trans-title=Castles of Kiev: Legends were Richard's Castle, the House of Chimeras, Baron Steingel's castle |work=Interesniy Kiyev |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20061104033503/http://www.interesniy.kiev.ua/old/architecture/buildings/2 |archive-date=2006-11-04 }} * {{Cite web|url=http://brmr.livejournal.com/628752.html |title=Photogallery containing detailed images |publisher=[[LiveJournal]] |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20130814171250/http://brmr.livejournal.com/628752.html |archive-date=2013-08-14 }} * {{Cite web|url=http://www.domparketa.com.ua/ru/gallery/dom_ximer/ |title=Interior photogallery |work=domparketa.com.ua |language=ru |url-status=dead |archive-url=https://web.archive.org/web/20060829142840/http://domparketa.com.ua/ru/gallery/dom_ximer/ |archive-date=2006-08-29 }} syk2ys61817m3eaef53xttwbdd2gezu ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ/ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು/ಪತ್ರಾಗಾರೊ/2026 4 24597 366091 365112 2026-08-20T22:52:17Z MABot 170 Bot: Archiving 1 thread from [[ವಿಕಿಪೀಡಿಯ:ಚಾವಡಿ (ತಾಂತ್ರಿಕ ಸುದ್ದಿಲು)]] 366091 wikitext text/x-wiki == <span lang="en" dir="ltr">Tech News: 2026-03</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W03"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/03|Translations]] are available. '''Weekly highlight''' * The Wikimedia Foundation has shared some guiding questions for the July 2026–June 2027 Annual Plan on [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2026-2027/Product & Technology OKRs|Meta]] and ''[[diffblog:2025/12/10/shaping-wikimedia-foundations-2026-2027-annual-goals-key-questions-for-the-wikimedia-movement/|Diff]]''. These focus on global trends, faster and healthier experimentation, better support for newcomers, strengthening editors and advanced users, improving collaboration across projects, and growing and retaining readership. Feedback and ideas are welcome on the [[m:Talk:Wikimedia Foundation Annual Plan/2026-2027|talk page]]. '''Updates for editors''' * As part of the current work of Community Tech team on the [[m:Special:MyLanguage/Community Wishlist/W372|Multiple watchlists]] project, the display of [[Special:EditWatchlist|EditWatchlist]] will be updated as a first step towards multiple watchlists. Additionally, the pagination on [[Special:Search|Search]] will be updated too, as a part of the work on the [[m:Special:MyLanguage/Community Wishlist/W186|Revamp pagination / page navigation]] wish. [https://phabricator.wikimedia.org/T411596] * [[m:Special:GlobalWatchlist|The Global Watchlist]] is a MediaWiki [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] that lets you see your watchlists from different wikis on the same page. It was recently updated to look more like the regular [[Special:Watchlist|Watchlist]], such as preparing it for temporary accounts in IP masking (including rerouting user links to contributions pages), making page titles bold, and opening links in edit summaries and tags in new browser tabs. [https://phabricator.wikimedia.org/T398361][https://phabricator.wikimedia.org/T298919][https://phabricator.wikimedia.org/T273526][https://phabricator.wikimedia.org/T286309] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:28}} community-submitted {{PLURAL:28|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where global blocks did not have the option to disable sending emails, has now been fixed, and will be available for use in the week of January 13. [https://phabricator.wikimedia.org/T401293] '''Updates for technical contributors''' * The [[mw:Special:MyLanguage/VisualEditor/Citation tool|VisualEditor citation tool]] and [[mw:Special:MyLanguage/Help:Reference Previews|Reference Previews]] now support "map" as a reference type. [https://phabricator.wikimedia.org/T411083] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.10|MediaWiki]]/[[mw:MediaWiki 1.46/wmf.11|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/03|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W03"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೧:೦೩, ೧೩ ಜನವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29907192 --> == <span lang="en" dir="ltr">Tech News: 2026-04</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W04"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/04|Translations]] are available. '''Updates for editors''' * The tray shown on [[Special:Diff|Special:Diff]] in mobile view has been redesigned. It is now collapsed by default, and incorporates a link to undo the edit being viewed, making it easier for mobile editors and reviewers to take action while keeping the interface uncluttered. [https://phabricator.wikimedia.org/T402297] * [[m:Special:GlobalWatchlist|The Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] continues to improve — it now automatically determines the text direction (ensuring correct display of sites with unusual domain names) and shows detailed descriptions for log actions. Later this week, a new permanent link for page creations and CSS classes for each entry element will be added. [https://phabricator.wikimedia.org/T412505][https://phabricator.wikimedia.org/T287929][https://phabricator.wikimedia.org/T262768][https://phabricator.wikimedia.org/T414135] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:32}} community-submitted {{PLURAL:32|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the previously observed issue in Vector 2022, where anchor link targets were obscured by the sticky header, has now been addressed. [https://phabricator.wikimedia.org/T406114] '''Updates for technical contributors''' * As mentioned in the [[m:Special:MyLanguage/Tech/News/2025/44|October 2025 deprecation announcement]], MediaWiki Interfaces team will begin sunsetting all transform endpoints containing a trailing slash from the MediaWiki REST API the week of January 26. Changes are expected to roll out to all wikis on or before January 30th. All API users currently calling them are encouraged to transition to the non-trailing slash versions. Both endpoint variations can be found, compared, and tested using the [https://test.wikipedia.org/wiki/Special:RestSandbox REST Sandbox]. If you have questions or encounter any problems, please file a ticket in Phabricator to the [https://phabricator.wikimedia.org/project/view/6931/ #MW-Interfaces-Team board]. * Interactive reference documentation for the [[mw:Special:MyLanguage/Wikimedia REST API|Wikimedia REST API]] has moved. Requests to API docs previously hosted through [[mw:Special:MyLanguage/RESTBase|RESTBase]] (e.g.: <code dir=ltr>https://en.wikipedia.org/api/rest_v1/</code>) are now redirected to the [[w:en:Special:RestSandbox|REST Sandbox]]. * The [[mw:Special:MyLanguage/Wikidata Platform|WMF Wikidata Platform team]] (WDP) has published its [[d:Special:MyLanguage/Wikidata:Wikidata Platform team/Newsletter|January 2026 newsletter]]. It includes updates on the legacy full-graph endpoint decommissioning, the User-Agent policy change, the monthly Blazegraph migration office hours, and efforts to reduce regressions caused by the legacy endpoint shutdown. As a reminder, you can [[m:Special:MyLanguage/Global message delivery/Targets/WDP team updates|subscribe to the WDP newsletter]]! * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.12|MediaWiki]] '''Meetings and events''' * The [[mw:Wikimedia Hackathon Northwestern Europe 2026|Wikimedia Hackathon Northwestern Europe 2026]] will take place on 13-14 March 2026 in Arnhem, the Netherlands. Applications opened mid-December and will close soon or when capacity is reached. It's a two-day, technically oriented hackathon bringing together Wikimedians from the region. Hope to see you there! '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/04|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W04"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೧:೫೯, ೨೦ ಜನವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29943403 --> == <span lang="en" dir="ltr">Tech News: 2026-05</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W05"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/05|Translations]] are available. '''Updates for editors''' * Wikimedia Foundation invites comments on [[m:Special:MyLanguage/Product and Technology Advisory Council/Year1 Reflections and Proposed Way Forward 2026 Update|proposed future]] of the [[:m:Special:MyLanguage/Product and Technology Advisory Council|Product and Technology Advisory Council]] until 28 February. * All users with registered accounts can now use passkeys for [[m:Special:MyLanguage/Help:Two-factor authentication|two-factor authentication]] (2FA). Passkeys are a simple way to log in without using a second device. They verify the user's identity using a fingerprint, face scan, or a PIN code. To set up a passkey, first set up a regular 2FA method. Currently, to log in with a passkey, users must also use a password. Later this quarter, passwordless login will allow users to log in with a single click and a passkey. Users with advanced rights will also be required to have 2FA enabled. This is part of the [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|Account Security]] project. * Unregistered contributors on blocked IPs or blocked IP ranges can now interact on-wiki to appeal a block by creating a temporary account to appeal a block on the user talk page, unless the "prevent this user from editing their own talk page" is enabled. This solves the problem of logged-out users unable to use the default unblock process via user talk page. [https://phabricator.wikimedia.org/T398673] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:20}} community-submitted {{PLURAL:20|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the Two-Factor Authentication (2FA) methods description on the management page has been updated. It is now clearer and easier for users to understand and make use of. [https://phabricator.wikimedia.org/T332385] '''Updates for technical contributors''' * A new AbuseFilter variable, <code>account_type</code>, has been added to provide a reliable way to determine the account type being created in the <code>createaccount</code> and <code>autocreateaccount</code> actions. As part of this change, the variable <code>accountname</code> has been renamed to <code>account_name</code>, and <code>accountname</code> is now deprecated. Edit filter managers should update any filters that use hardcoded account type checks or the deprecated variable. [https://phabricator.wikimedia.org/T414049] * Image thumbnails that are requested in non-standard sizes, and using non-standard methods such as direct requests to <code dir=ltr><nowiki>upload.wikimedia.org/…</nowiki></code> will stop working in the near future. This change is to prevent ongoing external abuse by web-scrapers and bots. Some users with custom CSS/JS, Interface Admins who can fix gadgets and local skins, and Tool-authors, will need to update their code to use standard thumbnail sizes. [[phab:T414805|Details, search-links, and examples of how to fix them, are available in the task]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.13|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/05|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W05"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೨:೪೭, ೨೭ ಜನವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:UOzurumba (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=29969530 --> == <span lang="en" dir="ltr">Tech News: 2026-06</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W06"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/06|Translations]] are available. '''Updates for editors''' * The "{{int:pageinfo-toolboxlink}}" feature, which gives validating information about a page ([{{fullurl:{{FULLPAGENAME}}|action=info}} example]), now automatically includes a table of contents. If there is a local [[{{ns:8}}:Pageinfo-header]] page created by individual users, it can now be removed. [https://phabricator.wikimedia.org/T363726] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, VisualEditor previously added bold or italic formatting inside link descriptions, making the wikicode complex. This has now been fixed. [https://phabricator.wikimedia.org/T409669] '''Updates for technical contributors''' * There was no XML dump on 20 January. Additionally, from now on, dumps will be generated once per month only. [https://phabricator.wikimedia.org/T414389] * The MediaWiki Interfaces team removed support for all transform endpoints containing a trailing slash from the [https://www.mediawiki.org/wiki/Special:MyLanguage/API:REST%20API MediaWiki REST API]. All API users currently calling those endpoints are encouraged to transition to the non-trailing slash versions. If you have questions or encounter any problems, please file a ticket in phabricator to the [https://phabricator.wikimedia.org/project/view/6931/ #MW-Interfaces-Team board]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.14|MediaWiki]] '''Weekly highlight''' * Users are reminded that the Wikimedia Foundation has shared some guiding questions for the July 2026–June 2027 Annual Plan on [[m:Special:MyLanguage/Wikimedia Foundation Annual Plan/2026-2027/Product & Technology OKRs|Meta]] and ''[[diffblog:2025/12/10/shaping-wikimedia-foundations-2026-2027-annual-goals-key-questions-for-the-wikimedia-movement/|Diff]]''. These focus on global trends, faster and healthier experimentation, better support for newcomers, strengthening editors and advanced users, improving collaboration across projects, and growing and retaining readership. Feedback and ideas are welcome on the [[m:Talk:Wikimedia Foundation Annual Plan/2026-2027|talk page]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/06|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W06"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೩:೧೩, ೨ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30000986 --> == <span lang="en" dir="ltr">Tech News: 2026-07</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W07"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/07|Translations]] are available. '''Updates for editors''' * [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Wishlist item]] Logged-in contributors who manage large or complex watchlists can now organise and filter watched pages in ways that improve their workflows with the new [[mw:Special:MyLanguage/Help:Watchlist labels|Watchlist labels]] feature. By adding custom labels (for example: pages you created, pages being monitored for vandalism, or discussion pages) users can more quickly identify what needs attention, reduce cognitive load, and respond more efficiently. This improves watchlist usability, especially for highly active editors. * A new feature available on [[Special:Contributions|Special:Contributions]] shows [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts|temporary accounts]] that are likely operated by the same person, and so makes patrolling less time-consuming. Upon checking contributions of a temporary account, users with access to temporary account IP addresses can now see a view of contributions from the related temporary accounts. The feature looks up all the IPs associated with a given temporary account within the data retention period and shows all the contributions of all temporary accounts that have used these IPs. [[mw:Special:MyLanguage/Trust and Safety Product/Temporary Accounts#February 2026: Improvements to the patroller tooling|Learn more]]. [https://phabricator.wikimedia.org/T415674] * When editors preview a wikitext edit, the reminder box that they are only seeing a preview (which is shown at the top), now has a grey/neutral background instead of a yellow/warning background. This makes it easier to distinguish preview notes from actual warnings (for example, edit conflicts or problematic redirect targets), which will now be shown in separate warning or error boxes. [https://phabricator.wikimedia.org/T414742] * The [[m:Special:GlobalWatchlist|Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] continues to improve — it now properly supports more than one Wikibase site, for example both [[d:|Wikidata]] and [[testwikidata:|testwikidata]]. In addition, issues regarding text direction have been fixed for users who prefer Wikidata or other Wikibase sites in right-to-left (RTL) languages. [https://phabricator.wikimedia.org/T415440][https://phabricator.wikimedia.org/T415458] * The automatic "magic links" for ISBN, RFC, and PMID numbers have been [[mw:Special:MyLanguage/Help:Magic links|deprecated in wikitext since 2021]] due to inflexibility and difficulties with localization. Several wikis have successfully replaced RFC and PMID magic links with equivalent external links, but a template was often required to replace the functionality of the ISBN magic link. There is now a new [[mw:Special:MyLanguage/Help:Magic words#isbn|built-in parser function]] <code dir=ltr><nowiki>{{#isbn}}</nowiki></code> available to replace the basic functionality of the ISBN magic link. This makes it easier for wikis who wish to migrate off of the deprecated magic link functionality to do so. [https://phabricator.wikimedia.org/T145604] * Two new wikis have been created: ** a {{int:project-localized-name-group-wikipedia}} in [[d:Q35401|Jju]] ([[w:kaj:|<code>w:kaj:</code>]]) [https://phabricator.wikimedia.org/T413283] ** a {{int:project-localized-name-group-wikipedia}} in [[d:Q1186896|Nawat]] ([[w:ppl:|<code>w:ppl:</code>]]) [https://phabricator.wikimedia.org/T413273] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:23}} community-submitted {{PLURAL:23|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. '''Updates for technical contributors''' * A new global user group has been created: [[{{int:grouppage-local-bot}}|{{int:group-local-bot}}]]. It will be used internally by the software to allow community bots to bypass rate limits that are applied to abusive [[w:en:Web scraping|web scrapers]]. Accounts that are approved as bots on at least one Wikimedia wiki will be automatically added to this group. It will not change what user permissions the bot has. [https://phabricator.wikimedia.org/T415588] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.15|MediaWiki]] '''Meetings and events''' * The [[mw:Special:MyLanguage/MediaWiki Users and Developers Conference Spring 2026|MediaWiki Users and Developers Conference, Spring 2026]] will be held March 25–27 in Salt Lake City, USA. This event is organized by and for the third-party MediaWiki community. You can propose sessions and register to attend. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/AZBWVI46SDEB65PGR5J6E4TYOQQEZXM7/] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/07|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W07"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೫:೦೦, ೧೦ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:Quiddity (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30026671 --> == <span lang="en" dir="ltr">Tech News: 2026-08</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W08"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/08|Translations]] are available. '''Weekly highlight''' * The [[mw:Special:MyLanguage/Wikimedia Site Reliability Engineering|SRE Team]] will be performing a cleanup of Wikimedia's [[m:Special:MyLanguage/Etherpad|Etherpad]] instance, the web-based editor for real-time collaborative document editing. All pads will be permanently deleted after 30 April, 2026 – if there are still migration projects in progress at that point the team can revisit the date on a case by case basis. Please create local backups of any content you wish to keep, as deleted data cannot be recovered. This cleanup helps reduce database size and minimize infrastructure footprint. Etherpad will continue to support real-time collaboration, but long-term storage should not be expected. Additional cleanups may occur in the future without prior notice. [https://phabricator.wikimedia.org/T415237] '''Updates for editors''' * The Information Retrieval team will be launching an [[mw:Special:MyLanguage/Readers/Information Retrieval/Phase 1|Android mobile app experiment]] that tests hybrid search capabilities which can handle both semantic and keyword queries. The improvement of on-platform search will enable readers to find what they’re looking for directly on Wikipedia more easily. The experiment will first be launched on Greek Wikipedia in late February, followed by English, French, and Portuguese in March. [https://diff.wikimedia.org/2026/01/08/semantic-search-making-it-easier-to-find-the-information-readers-want/ Read more] on Diff blog. [https://www.mediawiki.org/wiki/Readers/Information_Retrieval] * The Reader Growth team will run [[mw:Special:MyLanguage/Readers/Reader Growth/WE3.10.2 Mobile Table of Contents|an experiment]] for mobile web users, that adds a table of contents and automatically expands all article sections, to learn more about navigation issues they face. The test will be available on Arabic, Chinese, English, French, Indonesian, and Vietnamese Wikipedias. * Previously, site notices ([[{{ns:8}}:Sitenotice]] and [[{{ns:8}}:Anonnotice]]) would only render on the desktop site. Now, they will render on all platforms. Users on mobile web will now see these notices and be informed. Site administrators should be prepared to test and fix notices on mobile devices to avoid interference with articles. To opt out, interface admins can add <code dir="ltr">#siteNotice { display: none; }</code> to [[{{ns:8}}:Minerva.css]]. [https://phabricator.wikimedia.org/T138572][https://phabricator.wikimedia.org/T416644] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:19}} community-submitted {{PLURAL:19|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue on [[Special:RecentChanges|Special:RecentChanges]] has been fixed. Previously, clicking hide in the active filters caused the "view new changes since…" button to disappear, though it should have remained visible. The button now behaves as expected. [https://phabricator.wikimedia.org/T406339] '''Updates for technical contributors''' * New documentation is now available to help editors debug on-site search features. It supports troubleshooting when pages do not appear in results, when ranking seems unexpected, and when you need to inspect what content is being indexed, helping make search behavior easier to understand and analyze. [[mw:Help:CirrusSearch/Debug|Learn more]]. [https://phabricator.wikimedia.org/T411169] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.16|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/08|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W08"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೦:೪೭, ೧೭ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30086330 --> == <span lang="en" dir="ltr">Tech News: 2026-09</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W09"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/09|Translations]] are available. '''Weekly highlight''' * [[mw:Special:MyLanguage/Edit check/Reference Check|Reference Check]] has been deployed to English Wikipedia, completing its rollout across all Wikipedias. The feature prompts newcomers to add a citation before publishing new content, helping reduce common citation-related reverts and improve verifiability. In A/B testing, the impact was substantial: newcomers shown Reference Check were approximately 2.2 times more likely to include a reference on desktop and about 17.5 times more likely on mobile web. [https://analytics.wikimedia.org/published/reports/editing/reference_check_ab_test_report_final_2025.html] '''Updates for editors''' * The [[mw:Special:MyLanguage/Extension:InterwikiSorting|InterwikiSorting extension]], which allowed for the [[m:Special:MyLanguage/Interwiki sorting order|sorting of interwiki links]], has been undeployed from Wikipedia. As a result, editors who had enabled interwiki link sorting in non-compact mode (full list format) will now see links reordered. The links moving forward will be listed in the alphabetical order of language code. [https://phabricator.wikimedia.org/T253764] * Later this week, people who are editing a page-section using the mobile visual editor, will notice a new "Edit full page" button. When tapped, you will be able to edit the entire article. This helps when the change you want to make is outside the section you initially opened. [https://phabricator.wikimedia.org/T387175][https://phabricator.wikimedia.org/T409112] * [[mw:Special:MyLanguage/Readers/Reader Experience|The Reader Experience team]] is inviting editors to assess whether dark mode should still be considered "beta" on their wiki, based on their experience of how well it functions on desktop and mobile. If the feature is deemed mature, editors can update the interface messages in <code dir=ltr>MediaWiki:skin-theme-description</code> and <code dir=ltr>MediaWiki:Vector-night-mode-beta-tag</code> to indicate that dark mode is ready and no longer considered beta. * The improved [[mw:Wikimedia_Apps/Team/iOS/Activity_Tab|Activity tab]] which displays user-insights is now available to all users of the Wikipedia iOS app (version 7.9.0 and later). Following earlier A/B testing that showed higher account creation among users with access to the feature, it has been rolled out to 100% of users along with some updates. The Activity tab now shows your edited articles in the timeline, offers editing impact insights like contribution counts and article view trends, and customization options to improve in-app experience for users. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, a bug that prevented [[mw:Special:MyLanguage/Extension:DiscussionTools|DiscussionTools]] from working on mobile has now been fixed, restoring full functionality. [https://phabricator.wikimedia.org/T415303] '''Updates for technical contributors''' * The [[m:Special:GlobalWatchlist|Global Watchlist]] lets you view your watchlists from multiple wikis on one page. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] that makes this possible continues to improve. The latest upgrade is the inclusion of a [[mw:Extension:GlobalWatchlist#hook|new hook]], <code dir=ltr>ext.globalwatchlist.rebuild</code>, which fires after each watchlist rebuild. This allows you to run gadgets and user scripts for the Special page. [https://phabricator.wikimedia.org/T275159] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.17|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/09|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W09"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೦:೩೩, ೨೪ ಪೆಬ್ರವರಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30119102 --> == <span lang="en" dir="ltr">Tech News: 2026-10</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W10"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/10|Translations]] are available. '''Weekly highlight''' * Wikipedia 25 [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments|Birthday mode]] is now live on Betawi, Breton, Chinese, Czech, Dutch, English, French, Gorontalo, Indonesian, Italian, Luxembourgish, Madurese, Sicilian, Spanish, Thai, and Vietnamese Wikipedias! This limited-time campaign feature celebrates 25 years of Wikipedia with a birthday mascot, Baby Globe. When turned on, Baby Globe is shown on [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments/article configuration|~2,500 articles]], waiting to be discovered by readers. Communities can choose to turn Birthday mode on by getting consensus from their community and asking an admin to enable the feature and customize it via [[m:Special:MyLanguage/Wikipedia 25/Easter egg experiments#Community Configuration Demo|community configuration]] on the local wiki. '''Updates for editors''' * [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|Sub-referencing]], a new feature to re-use references with different details has been released to Swedish Wikipedia, Polish Wikipedia and [[:phab:T418209|a couple of other wikis]]. You can [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#test|try the feature]] on these projects or on testwiki and [https://en.wikipedia.beta.wmcloud.org/wiki/Sub-referencing betawiki]. Learnings from the first pilot wiki German Wikipedia have been [[:m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing/Learnings|published in a report]]. Reach out to the Wikimedia Deutschland team if you are [[:m:Talk:WMDE Technical Wishes/Sub-referencing#Pilot wikis|interested in becoming a pilot wiki]]. * [[mw:Special:MyLanguage/Help:Edit check#Paste check|Paste Check]] will become available at all Wikipedias this week. The feature prompts newcomers who are pasting text they are not likely to have written into VisualEditor to consider whether doing so risks a copyright violation. Paste Check [[mw:Special:MyLanguage/Edit check/Tags|tags]] all edits where it is shown for potential review. Local administrators can configure various aspects of the feature via [[{{#special:EditChecks}}]]. [[mw:Special:MyLanguage/Edit check/Paste Check#A/B Experiment|Research]] across 22 wikis found that Paste Check resulted in an 18% decrease in relative reverted-edits compared to the control group. Translators can [https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-visualeditor-ve-mw-editcheck&filter=&optional=1&action=translate help to localize] this and related features. * The [[mw:Special:MyLanguage/Readers/Reader Experience|Reader Experience team]] will be standardizing the user menu in the top right for all mobile users so that it is closer to the desktop experience. Currently this user menu is only visible to users with Advanced Mobile Controls (AMC) turned on. The only change is that a couple buttons previously in the left-side menu will move to the top right for users who do not have AMC turned on. This change is expected to go out March 9 and seeks to improve the user interface. [https://phabricator.wikimedia.org/T413912] * Starting in the week of March 2, the emails sent out when an email address was added, removed, or changed for an account will switch to a substantially nicer and clearer HTML email from the prior plaintext one. [https://phabricator.wikimedia.org/T410807] * Notifications are currently limited to 2,000 historic entries per user, and extend back to 2013 when the feature was released. This is going to be changed to only store Notifications from the last 5 years, but up to 10,000 of them. This will help with long-term infrastructure health and help to prevent more recent notifications from disappearing too soon. [https://phabricator.wikimedia.org/T383948] * The [[m:Special:GlobalWatchlist|Global Watchlist]] which lets you view your watchlists from multiple wikis on a single page continues to see improvements. The latest update improves label usage experience. The [[mw:Special:MyLanguage/Extension:GlobalWatchlist|extension]] now allows activating the [[mw:Special:MyLanguage/Manual:Language#Fallback languages|language fallback system]] for Wikidata items without labels in the viewed language, and showing those labels in the user’s preferred Wikidata language if no <code dir=ltr>uselang=</code> URL parameter is provided. [https://phabricator.wikimedia.org/T373686][https://phabricator.wikimedia.org/T416111] * The Wikipedia Android team has started a beta test of [[mw:Special:MyLanguage/Readers/Information Retrieval/Phase 1|hybrid search]] on Greek Wikipedia. Hybrid search capabilities can handle both semantic and keyword queries enabling readers to find what they’re looking for directly on Wikipedia more easily. * For security reasons, members of certain user groups are [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|required to have two-factor authentication]] (2FA) enabled. Currently, 2FA is required to use the group, but not to be a member of it. Given that this model still has some vulnerabilities, the situation will [[phab:T418580|gradually change in March]]. Members of these groups will be unable to disable last 2FA method on their account, and it will be impossible to add users without 2FA to these groups. Users will still be able to add new authentication methods or remove them, as long as at least one method is continuously enabled. In the second half of March, users without 2FA will be removed from these groups. This applies to: CentralNotice administrators, checkusers, interface administrators, suppressors, Wikidata staff, Wikifunctions staff, WMF Office IT and WMF Trust & Safety. Nothing will change for other users. See the linked task for deployment schedule. [https://phabricator.wikimedia.org/T418580] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:27}} community-submitted {{PLURAL:27|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue preventing users from creating an instance in [https://www.wikibase.cloud/ Wikibase.cloud] has now been fixed. [https://phabricator.wikimedia.org/T416807] '''Updates for technical contributors''' * To help ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]], over the next month the Wikimedia Foundation will implement global API rate limits across our APIs. In early March, stricter limits will be applied to unidentified requests from outside Toolforge/WMCS and API requests that are made from web browsers. In April, higher limits will be applied to identified traffic. These limits are intentionally set as high as possible to minimise impact on the community. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]]. * The Wikidata Query Service Linked Data Fragment (LDF) endpoint will be decommissioned in February. This endpoint served limited traffic, which was successfully migrated to other data access methods that were better suited to support existing use cases. The hardware used to support the LDF endpoint will be reallocated to support the ongoing backend migration efforts. [https://phabricator.wikimedia.org/T415696] * The new Parsoid parser [[mw:Special:MyLanguage/Parsoid/Parser Unification/Updates|continues to be deployed to additional wikis]], improving platform sustainability and making it easier to introduce new reading and editing features. Parsoid is now the default parser on 488 WMF wikis (268 Wikipedias), now covering more than 10% of all Wikipedia page views. * The process and criteria for [[Special:MyLanguage/Wikimedia Enterprise#Access|requesting exceptional access]] to the high volume feed of the ''Wikimedia Enterprise'' APIs (at no cost for mission-aligned usecases), [[m:Talk:Wikimedia Enterprise#Exceptional access criteria|have now been published]]. This is to provide more thorough and clearer documentation for users. * [https://techblog.wikimedia.org/ Tech Blog], the blog dedicated to the Wikimedia technical community [https://techblog.wikimedia.org/2026/02/24/a-tech-blog-diff/ will be migrating] to [[diffblog:|Diff]], the community news and event blog. The migration should be complete in April 2026, after which new posts will be accepted for publishing. Readers will be able to access posts – old and new – on the landing page at https://diff.wikimedia.org/techblog. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.18|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/10|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W10"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೩:೨೧, ೨ ಮಾರ್ಚಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30137798 --> == <span lang="en" dir="ltr">Tech News: 2026-11</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W11"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/11|Translations]] are available. '''Weekly highlight''' * [[m:Special:MyLanguage/Tech/Server switch|All wikis will be read-only]] for a few minutes on Wednesday, 25 March 2026 at [https://zonestamp.toolforge.org/1774450800 15:00 UTC]. This is for the datacenter server switchover backup tests, [[wikitech:Deployments/Yearly calendar|which happen twice a year]]. During the switchover, all Wikimedia website traffic is shifted from one primary data center to the backup data center to test availability and prevent service disruption even in emergencies. * Last week, all wikis had 2 hours of read-only time, and extended unavailability for user-scripts and gadgets. This was due to a security incident which has since been resolved. Work is ongoing to prevent re-occurrences. For current information please see the [[m:Steward's noticeboard#Statement on Meta about today's user script security incident|post on the Stewards' noticeboard]] ([[m:Special:MyLanguage/Wikimedia Foundation/Product and Technology/Product Safety and Integrity/March 2026 User Script Incident|translations]]). '''Updates for editors''' * Users facing multiple blocks on mobile will now see the reasons for each block separately, instead of a generic message. This helps them understand why they are blocked and what steps they can take to resolve the issue. For example, users affected for using common VPNs (such as [[Special:MyLanguage/Apple iCloud Private Relay|iCloud Private Relay]]) will receive clearer guidance on what they need to do to start editing again. [https://phabricator.wikimedia.org/T357118] * Later this week, [[mw:Special:MyLanguage/VisualEditor/Suggestion Mode|Suggestion Mode]] will become available as a beta feature within the visual editor at all Wikipedias. This feature proactively suggests various types of actions that people can consider taking to improve Wikipedia articles, and learn about related guidelines. The feature is locally configurable, and can also be locally expanded with custom Suggestions. Current settings can be seen at [[Special:EditChecks]] and there are [[mw:Special:MyLanguage/Help:Suggestion mode#For administrators %E2%80%93 local customization|instructions for how administrators can customize]] the links to point to local guidelines. The feature is connected to [[mw:Special:MyLanguage/Help:Edit check|Edit check]] which suggests improvements while someone is writing new content. In the future, the Editing team plans to evaluate the feature's impact with newcomers through a controlled experiment. [https://phabricator.wikimedia.org/T404600] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:23}} community-submitted {{PLURAL:23|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where the cursor became misaligned during the use of CodeMirror’s syntax highlighting, which makes wikitext and code easier to read, has now been fixed. This problem specifically affected users who defined a font rule in a custom stylesheet while creating a new topic with DiscussionTools. [https://phabricator.wikimedia.org/T418793] '''Updates for technical contributors''' * API rate limiting update: To help ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]], global API rate limits will be applied this week to requests without a compliant User-Agent that originate from outside Toolforge/WMCS and to unauthenticated requests made from web browsers. Higher limits will be applied to identified traffic in April. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]]. * The new GraphQL API has been released. The API was developed as a flexible alternative to select features of the Wikidata Query Service (WDQS), to improve developer experience and foster adaptability, and efficient data access. Try it out and [[d:Wikidata:Wikibase GraphQL#Feedback and development|give feedback]]. You can also [https://greatquestion.co/wikimediadeutschland/GraphQLAPI/apply sign up for usability tests]. * The [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group|PTAC Unsupported Tools Working Group]] continued improvements to [[commons:Special:MyLanguage/Commons:Video2commons#|Video2Commons]] in February, with fixes addressing authentication errors, large-file handling, task queue visibility, and clearer upload behavior. Work is still ongoing in some areas, including changes related to deprecated server-side uploads. Read [[m:Special:MyLanguage/Product and Technology Advisory Council/Unsupported Tools Working Group#February 2026|this update]] to learn more. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.19|MediaWiki]] '''In depth''' * The Article Guidance team invites experienced Wikipedia editors from selected [[mw:Special:MyLanguage/Article guidance/Pilot wikis and collaborators#Collaborators|pilot wikis]] and interested contributors from other Wikipedias to fill out this questionnaire which is available in [https://docs.google.com/forms/d/e/1FAIpQLSfmLeVWnxmsCbPoI_UF2jyRcn73WRGWCVPHzerXb4Cz97X_Ag/viewform English], [https://docs.google.com/forms/d/e/1FAIpQLSd6rzr4XXQw8r4024fE3geTPFe13M_6w7Mitj-YJi0sOlWTAw/viewform?usp=header Arabic], [https://docs.google.com/forms/d/e/1FAIpQLSdok3-RfB18lcugYTUMGkpwmqG_8p760Wv4dCXitOXOszjUDw/viewform?usp=header Bengali], [https://docs.google.com/forms/d/e/1FAIpQLSfjTfYp4jEo0akA4B1e-Nfg3QZPCudUjhJzHzzDi6AHyAaMGA/viewform?usp=header Japanese], [https://docs.google.com/forms/d/e/1FAIpQLScteVoI29Aue4xc72dekk-6RYtvmMgQxzMI900UOawrFrSTWg/viewform?usp=header Portuguese], [https://docs.google.com/forms/d/e/1FAIpQLSetdxnYwL3ub2vqA7awCg5hJZPMIYcDPaiTe12rY9h0GYnVlw/viewform?usp=header Persian], and [https://docs.google.com/forms/d/e/1FAIpQLScNvfJF-Ot-4pzA4qAN771_0QDJ4Li19YcUsaTgSKW8Nc7U_Q/viewform?usp=header Turkish]. Your answers will help the team customize guidance for less experienced editors and help them learn community policies and practices while creating an article. Learn more [[mw:Special:MyLanguage/Article guidance|on the project page]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/11|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W11"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೦:೨೨, ೧೦ ಮಾರ್ಚಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30213008 --> == <span lang="en" dir="ltr">Tech News: 2026-12</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W12"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/12|Translations]] are available. '''Updates for editors''' * The [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature, also known as [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror 6]], has been used for wikitext syntax highlighting since November 2024. It will be promoted out of beta by May 2026 in order to bring improvements and new [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Features|features]] to all editors who use the standard syntax highlighter. If you have any questions or concerns about promoting the feature out of beta, [[mw:Special:MyLanguage/Help talk:Extension:CodeMirror|please share]]. [https://phabricator.wikimedia.org/T259059] * Some changes to local user groups are performed by stewards on Meta-Wiki and logged there only. Now, interwiki rights changes will be logged both on Meta-Wiki and the wiki of the target user to make it easier to access a full record of user's rights changes on a local wiki. Past log entries for such changes will be backfilled in the coming weeks. [https://phabricator.wikimedia.org/T6055] * On wikis using [[m:Special:MyLanguage/Flagged Revisions|Flagged Revisions]], the number of pending changes shown on [[{{#Special:PendingChanges}}]] previously counted pages which were no longer pending review, because they have been removed from the system without being reviewed, e.g. due to being deleted, moved to a different namespace, or due to wiki configuration changes. The count will be correct now. On some wikis the number shown will be much smaller than before. There should be no change to the list of pages itself. [https://phabricator.wikimedia.org/T413016] * Wikifunctions composition language has been rewritten, resulting in a new version of the language. This change aims to increase service stability by reducing the orchestrator's memory consumption. This rewrite also enables substantial latency reduction, code simplification, and better abstractions, which will open the door to later feature additions. Read more about [[f:Special:MyLanguage/Wikifunctions:Status updates/2026-03-11|the changes]]. * Users can now sort search results alphabetically by page title. The update gives an additional option to finding pages more easily and quickly. Previously, results could be sorted by Edit date, Creation date, or Relevance. To use the new option, open 'Advanced Search' on the search results page and select 'Alphabetically' under 'Sorting Order'. [https://phabricator.wikimedia.org/T403775] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:28}} community-submitted {{PLURAL:28|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the bug that prevented UploadWizard on Wikimedia Commons from importing files from Flickr has now been fixed. [https://phabricator.wikimedia.org/T419263] '''Updates for technical contributors''' * A new special page, [[{{#special:LintTemplateErrors}}]], has been created to list transcluded pages that are flagged as containing lint errors to help users discover them easily. The list is sorted by the number of transclusions with errors. For example: [[{{#special:LintTemplateErrors}}/night-mode-unaware-background-color]]. [https://phabricator.wikimedia.org/T170874] * Users of the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature have been using [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]] for syntax highlighting when editing JavaScript, CSS, JSON, Vue and Lua content pages, for some time now. Along with promoting CodeMirror 6 out of beta, the plan is to replace CodeEditor as the standard editor for these content models by May 2026. [[mw:Special:MyLanguage/Help talk:Extension:CodeMirror|Feedback or concerns are welcome]]. [https://phabricator.wikimedia.org/T419332] * The [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] JavaScript modules will soon be upgraded to CodeMirror 6. Leading up to the upgrade, loading the <code dir=ltr>ext.CodeMirror</code> or <code dir=ltr>ext.CodeMirror.lib</code> modules from gadgets and user scripts was deprecated in July 2025. The use of the <code dir=ltr>ext.CodeMirror.switch</code> hook was also deprecated in March 2025. Contributors can now make their scripts or gadgets compatible with CodeMirror 6. See the [[mw:Special:MyLanguage/Extension:CodeMirror#Gadgets and user scripts|migration guide]] for more information. [https://phabricator.wikimedia.org/T373720] * The MediaWiki Interfaces team is expanding coverage of REST API module definitions to include [[mw:Special:MyLanguage/API:REST API/Extensions|extension APIs]]. REST API modules are groups of related endpoints that can be independently managed and versioned. Modules now exist for [https://phabricator.wikimedia.org/T414470 GrowthExperiments] and [https://phabricator.wikimedia.org/T419053 Wikifunctions] APIs. As we migrate extension APIs to this structure, documentation will move out of the main MediaWiki OpenAPI spec and REST Sandbox view, and will instead be accessible via module-specific options in the dropdown on the [https://test.wikipedia.org/wiki/Special:RestSandbox REST Sandbox] (i.e., [[{{#Special:RestSandbox}}]], available on all wiki projects). * The [[mw:Special:MyLanguage/Extension:Scribunto|Scribunto]] extension provides different pieces of information about the wiki where the module is being used via the [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual|mw.site]] library. Starting last week, the library also provides a [[mw:Special:MyLanguage/Extension:Scribunto/Lua reference manual#mw.site.wikiId|way]] of accessing the [[mw:Special:MyLanguage/Manual:Wiki ID|wiki ID]] that can be used to facilitate cross-wiki module maintenance. [https://phabricator.wikimedia.org/T146616] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.20|MediaWiki]] '''In depth''' * The [[m:Special:MyLanguage/Coolest Tool Award|2026 Coolest Tool Award]] celebrating outstanding community tools, is now open for nominations! Nominate your favorite tool using the [https://wikimediafoundation.limesurvey.net/435684?lang=en nomination survey] form by 23 March 2026. For more information on privacy and data handling, please see the [[foundation:Special:MyLanguage/Legal:Coolest_Tool_Award_2026_Survey_Privacy_Statement|survey privacy statement]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/12|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W12"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೧:೦೫, ೧೭ ಮಾರ್ಚಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30260505 --> == <span lang="en" dir="ltr">Tech News: 2026-13</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W13"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/13|Translations]] are available. '''Weekly highlight''' * Wikimedia site users can now log in without a password using passkeys. This is a secure method supported by fingerprint, facial recognition, or PIN. With this change, all users who opt for passwordless login will find it easier, faster, and more secure to log in to their accounts using any device. The new passkey login option currently appears as an autofill suggestion in the username field. An additional [[phab:T417120|"Log in with passkey" button]] will soon be available for users who have already registered a passkey. This update will improve security and user experience. The [[c:File:Passwordless_login_screencast.webm|screen recording]] demonstrates the passwordless login process step by step. * [[m:Special:MyLanguage/Tech/Server switch|All wikis will be read-only]] for a few minutes on Wednesday, 25 March 2026 at [https://zonestamp.toolforge.org/1774450800 15:00 UTC]. This is for the datacenter server switchover backup tests, [[wikitech:Deployments/Yearly calendar|which happen twice a year]]. During the switchover, all Wikimedia website traffic is shifted from one primary data center to the backup data center to test availability and prevent service disruption even in emergencies. '''Updates for editors''' * Wikimedia site users can now export their notifications older than 5 years using a [[toolforge:echo-chamber|new Toolforge tool]]. This will ensure that users retain their important notifications and avoid them being lost based on the planned change to delete notifications older than 5 years, as previously announced. [https://phabricator.wikimedia.org/T383948] * Wikipedia editors in Indonesian, Thai, Turkish, and Simple English now have access to Special:PersonalDashboard. This is an [[mw:Special:MyLanguage/Moderator Tools/Dashboard|early version of an experience]] that introduces newer editors to patrolling workflows, making it easier for them to move from making edits to participating in more advanced moderation work on their project. [https://phabricator.wikimedia.org/T402647] * The [[Special:Block]] now has two minor interface changes. Administrators can now easily perform indefinite blocks through a dedicated radio button in the expiry section. Also, choosing an indefinite expiry provides a different set of common reasons to select from, which can be changed at: [[MediaWiki:Ipbreason-indef-dropdown]]. [https://phabricator.wikimedia.org/T401823] * Mobile editors [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#Logged-out|at several wikis]] can now see an improved logged-out edit warning, thanks to the recent updates from the Growth team. These changes released last week are part of ongoing efforts and tests to enhance [[mw:Special:MyLanguage/Contributors/Account Creation Experiments|account creation experience on mobile]] and then increase participation. [https://phabricator.wikimedia.org/T408484] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:36}} community-submitted {{PLURAL:36|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the bug that prevented mobile web users from seeing the block information when affected by multiple blocks has been fixed. They can now see messages of all the blocks currently affecting them when they access Wikipedia. '''Updates for technical contributors''' * Images built using Toolforge will soon get the upgraded buildpacks version, bringing support for newer language versions and other upstream improvements and fixes. If you use Toolforge Build Service, review the recent [https://lists.wikimedia.org/hyperkitty/list/cloud-announce@lists.wikimedia.org/thread/EMYTA32EV2V5SQ2JIEOD2CL66YFIZEKV/ cloud-announce email] and update your build configuration as necessary to ensure your tools are compatible. [https://wikitech.wikimedia.org/w/index.php?title=Help:Toolforge/Building_container_images&oldid=2392097#Buildpack_environment_upgrade_process][https://phabricator.wikimedia.org/T380127] * The [https://api.wikimedia.org/wiki/Main_Page API Portal] documentation wiki will shut down in June 2026. API keys created on the API Portal will continue to work normally. api.wikimedia.org endpoints will be deprecated gradually starting in July 2026. Documentation on the API Portal is moving to [[mw:Wikimedia APIs|mediawiki.org]]. Learn more on the [[wikitech:API Portal/Deprecation|project page]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.21|MediaWiki]] '''In depth''' * [[m:Special:MyLanguage/WMDE Technical Wishes|WMDE Technical Wishes]] is considering improvements to [[m:WMDE Technical Wishes/References/VisualEditor automatic reference names|automatically generated reference names in VisualEditor]]. Please check out the [[m:WMDE Technical Wishes/References/VisualEditor automatic reference names#Proposed solutions|proposed solutions]] and participate in the [[m:Talk:WMDE Technical Wishes/References/VisualEditor automatic reference names#Request for comment|request for comment]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/13|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W13"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೨:೨೧, ೨೩ ಮಾರ್ಚಿ ೨೦೨೬ (IST) <!-- Message sent by User:UOzurumba (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30268305 --> == <span lang="en" dir="ltr">Tech News: 2026-14</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W14"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/14|Translations]] are available. '''Weekly highlight''' * The Beta version of [[abstract:|Abstract Wikipedia]] a new Wikimedia project which is language-independent, was launched last week. The project allows communities to build Wikipedia articles in their native language, which can be readily accessed by other users in their own languages. The wiki is powered by instructions from Wikifunctions and also based on structured content from Wikidata. [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-03-26|Read more]]. '''Updates for editors''' * The Growth team is running an A/B test to evaluate a clearer, more user-friendly message that promotes account creation on wikis. Currently when logged-out mobile users begin editing, they see a jarring warning message that can feel abrupt and discouraging. This also presents temporary account editing as the default rather than encouraging account creation. The test is running on ten Wikipedias, including Arabic, French, Spanish and German. [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#2. Improve logged-out warning message (T415160)|Read more]]. * The Wikimedia Apps team is inviting feedback on [[mw:Special:MyLanguage/Wikimedia Apps/Team/Future of Editing on the Mobile Apps|how editing should work on the Wikipedia mobile apps]]. The discussion focuses on improving how users access editing tools when they tap "Edit". This is part of a broader effort to convert readers who develop an interest in editing, to access a more user-friendly pathway to start contributing. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:45}} community-submitted {{PLURAL:45|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where citation fetching from the large newspaper archive [https://www.newspapers.com Newspapers.com] was no longer working, due to a block in [[mw:Special:MyLanguage/Citoid|Citoid]] requests, has now been fixed. [https://phabricator.wikimedia.org/T419903] '''Updates for technical contributors''' * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.22|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/14|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W14"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೦:೫೫, ೩೧ ಮಾರ್ಚಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30329462 --> == <span lang="en" dir="ltr">Tech News: 2026-15</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W15"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/15|Translations]] are available. '''Updates for editors''' * The [[mw:Special:MyLanguage/Help:Extension:CampaignEvents|CampaignEvents extension]] now includes a new group goal-setting feature, enabling organizers to set and track event goals such as the number of articles created and participating contributors in real time. Similarly, participants can work toward shared targets and see their collective impact as the event unfolds. The feature is now available on all Wikimedia wikis. Learn more in [[mw:Special:MyLanguage/Help:Extension:CampaignEvents/Registration/Collaborative contributions#Goal setting|the documentation]]. * [[File:Maki-gift-15.svg|12px|link=|class=skin-invert|Wishlist item]] The new [[mw:Special:MyLanguage/Help:Watchlist labels|watchlist labels]] feature (announced in [[m:Special:MyLanguage/Tech/News/2026/07|Tech News 2026-07]]) is now available via VisualEditor, the source editor, and the 'watchstar' (or watch link, for skins that don't have a star icon). Previously it was only possible to assign labels via [[Special:EditWatchlist|EditWatchlist]]. In all three places it is a new field following the expiry field. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:23}} community-submitted {{PLURAL:23|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where talk pages on mobile with Parsoid are unusable after empty section headers, has now been fixed. [https://phabricator.wikimedia.org/T419171] '''Updates for technical contributors''' * The [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|sub-referencing feature]], which lets editors add details to an existing reference without duplicating it, will be gradually rolled out to [[phab:T414094|more wikis]] later this year. Wikis using the [[mw:Special:MyLanguage/Reference Tooltips|Reference Tooltips]] gadget are encouraged to update their version (typically at [[m:MediaWiki:Gadget-ReferenceTooltips.js|MediaWiki:Gadget-ReferenceTooltips.js]] as shown [https://en.wikipedia.org/w/index.php?diff=1344408362 here]) to ensure compatibility. Other reference-related gadgets may also be affected. [https://phabricator.wikimedia.org/T416304] * All Wikinews editions will be closed and switched to read-only mode on 4 May 2026. Content will remain accessible, but no new edits or articles can be added. This closure was approved by the Board of Trustees of the Wikimedia Foundation following extended discussions. [[m:Wikimedia Foundation Board noticeboard#Board of Trustees Approves Closure of Wikinews|Read more]]. * The [[:mw:Special:MyLanguage/API:Action API|Action API]] has had several formats for requested output. One of them, <bdi lang="zxx" dir="ltr"><code><nowiki>format=php</nowiki></code></bdi>, is being removed soon. Please ensure your scripts or bots use the [[mw:Special:MyLanguage/API:Data formats#Output|JSON format]]. This removal should affect very few scripts and bots. [https://phabricator.wikimedia.org/T118538] * The [[Special:NamespaceInfo|Special:NamespaceInfo]] page now includes namespace aliases. For example "WP" for the "Project" ("Wikipedia") namespace on the German Wikipedia. [https://phabricator.wikimedia.org/T381455] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.23|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/15|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W15"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೧:೪೯, ೬ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30362761 --> == <span lang="en" dir="ltr">Tech News: 2026-16</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W16"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/16|Translations]] are available. '''Weekly highlight''' * Experienced editors are invited to [https://b24e11a4f1.catalyst.wmcloud.org/wiki/Main_Page test] the [[mw:Special:MyLanguage/Article guidance|Article guidance]] feature, designed to help less-experienced editors create well-structured, policy-compliant Wikipedia articles. Testing instructions are [[mw:Special:MyLanguage/Article guidance/Test feature guide|available]]. Also, after reviewing [https://b24e11a4f1.catalyst.wmcloud.org/wiki/Category:Pages_using_article_guidance the outlines], please provide feedback on the [[mw:Talk:Article guidance|project talk page]]. Based on your input, the feature will be refined and transferred to the pilot Wikipedias to translate and adapt. Check out [[c:File:Article Guidance workflow demo - April 2026.webm|the video]] explaining the feature. '''Updates for editors''' * On most wikis, all autoconfirmed users can now use [[Special:ChangeContentModel|Special:ChangeContentModel]] page to [[mw:Special:MyLanguage/Help:ChangeContentModel|create new pages with custom content models]], such as mass message lists, making custom page formats more accessible. Check [[Special:ListGroupRights|Special:ListGroupRights]] for the status of your wiki. [https://phabricator.wikimedia.org/T248294] * The Growth team has launched an [[mw:Special:MyLanguage/Contributors/Account_Creation_Experiments|account creation experiment]] to evaluate whether adding an account creation button to the mobile web header increases new account registrations and encourages more mobile users to contribute to the wikis. The experiment is currently live on Hindi, Indonesian, Bengali, Thai, and Hebrew Wikipedia, and targets 10% of logged-out mobile web users. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where VisualEditor could get stuck loading on Windows devices with animations turned off, has now been fixed. [https://phabricator.wikimedia.org/T382856] '''Updates for technical contributors''' * Starting later this week, {{int:group-abusefilter}} who have the [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]] beta feature enabled will have [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]] as the editor at [[Special:AbuseFilter|Special:AbuseFilter]]. This is part of the broader effort to make the user experience more consistent across all editors. [https://phabricator.wikimedia.org/T399673][https://phabricator.wikimedia.org/T419332] * Tools and bots that access the [[mw:Special:MyLanguage/Notifications/API|Notifications API]] (<bdi lang="zxx" dir="ltr"><code><nowiki>action=query&meta=notifications</nowiki></code></bdi>) will need to update their OAuth or BotPassword grants to also include access to private notifications. [https://phabricator.wikimedia.org/T421991] * Due to a library upgrade, listings on category pages may be displayed out of order starting on Monday, 20th April. A migration script will be run to correct this, and will take hours to days depending on the size of the wiki (up to a week for English Wikipedia). [https://phabricator.wikimedia.org/T422544] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.24|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/16|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W16"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೦:೪೮, ೧೩ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30380527 --> == <span lang="en" dir="ltr">Tech News: 2026-17</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W17"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/17|Translations]] are available. '''Weekly highlight''' * After two years of development, [[mw:Special:MyLanguage/Help:Extension:CodeMirror|{{int:codemirror-beta-feature-title}}]], also known as [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror 6]], is to be promoted out of beta on Tuesday, April 21. It brings better code and wikitext readability, reduction in typing errors, and other [[mw:Special:MyLanguage/Help:Extension:CodeMirror|benefits]] to all users of the standard syntax highlighter. A huge thank you to volunteer [https://phabricator.wikimedia.org/p/Bhsd/ Bhsd] who developed many of the new features, including [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Code folding|code folding]], [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Autocompletion|autocompletion]], and [[mw:Special:MyLanguage/Help:Extension:CodeMirror#Linting|linting]]. [https://phabricator.wikimedia.org/T259059] * A major update to the Wikipedia app for iOS is now rolling out, redesigning the interface to align with Apple's latest "Liquid Glass" visual design. [https://apps.apple.com/us/app/wikipedia/id324715238 Download the latest version] and explore the update. '''Updates for editors''' * [[mw:Special:MyLanguage/Readers/Reader Experience/WE3.3.4 Reading lists|Reading lists]] is a feature which allows readers to save articles to a list for reading later. This feature is now in beta on Arabic, French, Indonesian, Vietnamese, and Chinese Wikipedias and by default for all new accounts on all Wikipedias. * An experiment which explores extending [[mw:Special:MyLanguage/Readers/Reader Growth/Mobile page previews|Page Previews to mobile web]] will be launched in the week of April 20 on Arabic, English, French, Italian, Polish, and Vietnamese Wikipedias. Page Previews are pop-ups that display a thumbnail, lead paragraph, and a link to open the full article of a blue link, thereby improving content discovery. The feature is already available on desktop and in the apps. [[m:Special:MyLanguage/List of experiments in Product and Technology#Template|Read more about this experiment and others]]. * On several wikis, logged-in editors who haven't [[mw:Special:MyLanguage/Help:Email confirmation|confirmed their email addresses]] can now see a banner encouraging them to do so. Having the email address confirmed allows a user to restore access to the account if they lose it. [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security#Encouraging users to confirm their email addresses|Learn more]]. [https://phabricator.wikimedia.org/T421366] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:15}} community-submitted {{PLURAL:15|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where editing very large wiki pages in the 2017 wikitext editor caused slow loading, preview and scrolling lag, and performance issues when selecting, cutting, or pasting content, has now been fixed. [https://phabricator.wikimedia.org/T184857] '''Updates for technical contributors''' * As part of the promotion of [[mw:Special:MyLanguage/Help:Extension:CodeMirror|CodeMirror]] from a beta feature, all users will use [[mw:Special:MyLanguage/Extension:CodeMirror|CodeMirror]] instead of [[mw:Special:MyLanguage/Extension:CodeEditor|CodeEditor]] for syntax highlighting when editing JavaScript, CSS, JSON, Vue and Lua content pages. [https://phabricator.wikimedia.org/T419332] * The <code>mirrors.wikimedia.org</code> service for Debian and Ubuntu users will sunset and stop working on May 15. The resources for the service will be replaced with new and better options. Some users may need to switch to a different server which should take about a minute. [https://lists.wikimedia.org/hyperkitty/list/wikitech-l@lists.wikimedia.org/thread/LJYRIS4WB66HIRCAO4GIDTXCMDVZRBMA/ You can read more]. [https://phabricator.wikimedia.org/T416707] * The <bdi lang="zxx" dir="ltr"><code><nowiki>image</nowiki></code></bdi> and <bdi lang="zxx" dir="ltr"><code><nowiki>oldimage</nowiki></code></bdi> table will be removed from [[wikitech:Help:Wiki Replicas|wikireplicas]]. If your tools or queries access <bdi lang="zxx" dir="ltr"><code><nowiki>image</nowiki></code></bdi> or <bdi lang="zxx" dir="ltr"><code><nowiki>oldimage</nowiki></code></bdi> directly, please update them to use the <bdi lang="zxx" dir="ltr"><code><nowiki>file</nowiki></code></bdi> and <bdi lang="zxx" dir="ltr"><code><nowiki>filerevision</nowiki></code></bdi> table before 28 May. [https://phabricator.wikimedia.org/T28741] * Following the recent implementation of global API rate limits on unidentified traffic, the Wikimedia Foundation will continue efforts to ensure [[mw:Special:MyLanguage/MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]] by applying global limits to identified API traffic beginning the last week of April. These limits are intentionally set as high as possible to minimise impact on the community. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, see [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]] and [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits/FAQ|Frequently Asked Questions]]. * The [[mw:Special:MyLanguage/Attribution API|Attribution API]] is now available as a [[mw:Special:MyLanguage/Wikimedia APIs/Stability policy|beta]]. The API fetches information for crediting Wikimedia articles and media files wherever they are used. Reference documentation is available through the REST Sandbox special page available on all Wikimedia wikis (such as the [https://en.wikipedia.org/w/index.php?api=attribution.v0-beta&title=Special%3ARestSandbox REST sandbox on English Wikipedia]). Share your feedback on the [[mw:Talk:Attribution API|project talk page]]. * There is no new MediaWiki version this week. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/17|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W17"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೦:೩೦, ೨೦ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30432763 --> == <span lang="en" dir="ltr">Tech News: 2026-18</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W18"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/18|Translations]] are available. '''Updates for editors''' * There is a change in how new users are autoconfirmed that will improve anti-vandalism protection. Currently, users who have had an account for a few days and made a few edits are automatically added to the [[{{int:grouppage-autoconfirmed/{{CONTENTLANGUAGE}}}}|{{int:group-autoconfirmed}}]] group. This configuration tends to be exploited by some vandals, who create accounts and start to use them only after some time. To mitigate this, the configuration will be updated next week so that – for the purpose of becoming autoconfirmed – the account age will be counted from their first edit, instead of registration date. The numeric value of the age threshold will remain the same. This change will be deployed only to wikis which require at least one edit as part of the autoconfirmation conditions. [https://phabricator.wikimedia.org/T418484] * All Wikipedia users with new accounts and those who activated the "automatically enable most beta features" option in their preference can now use the [[mw:Special:MyLanguage/Readers/Reader Experience/WE3.3.4 Reading lists|reading lists]] beta feature to save articles for later reading. This helps organize reading interests in one place for convenient access. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the issue where infobox images have huge padding in Firefox, has been fixed. [https://phabricator.wikimedia.org/T423676] '''Updates for technical contributors''' * As a reminder, the global API rate limits will be applied this week to identified API traffic. This is to help ensure [[mw:MediaWiki Product Insights/Responsible Reuse|fair use of infrastructure]]. Bots running in Toolforge/WMCS or with the bot user right on any wiki should not be affected for now. However, all developers are advised to follow updated best practices. For more information, including the actual rate limits, see [[mw:Wikimedia APIs/Rate limits|Wikimedia APIs/Rate limits]] and [[mw:Wikimedia APIs/Rate limits/FAQ|Frequently Asked Questions]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.26|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/18|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W18"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೩:೩೬, ೨೭ ಎಪ್ರಿಲ್ ೨೦೨೬ (IST) <!-- Message sent by User:UOzurumba (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30458046 --> == <span lang="en" dir="ltr">Tech News: 2026-19</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W19"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/19|Translations]] are available. '''Weekly highlight''' * The [[mw:Special:MyLanguage/Article guidance|Article guidance]] team invites experienced editors of [[mw:Special:MyLanguage/Article guidance/Pilot wikis and collaborators|pilot Wikipedias]]—Arabic, Bangla, Japanese, Portuguese, Persian, Turkish, Simple English, Spanish, and French—to help translate and adapt [https://b24e11a4f1.catalyst.wmcloud.org/wiki/Category:Pages_using_article_guidance sample outlines]. These outlines will guide editors in creating clear, well-structured, and policy-compliant articles when using [https://b24e11a4f1.catalyst.wmcloud.org/wiki/Special:NewArticle the feature] once it is launched in May 2026. [[mw:Special:MyLanguage/Article guidance#Adapting a sample outline in a Wikipedia|Simple instructions]] on how to translate and adapt the outlines are available. '''Updates for editors''' * The [[:m:Special:MyLanguage/Product and Technology Advisory Council|Product and Technology Advisory Council]] has published [[:m:Special:MyLanguage/Product and Technology Advisory Council/May 2026 draft PTAC recommendation for feedback|draft recommendations]] on a model that affiliates can follow when contributing to the technical space. Community members are invited to provide feedback on the recommendation until May 8th [[:m:Talk:Product and Technology Advisory Council/May 2026 draft PTAC recommendation for feedback|on the talk page]]. * The number of available thumbnail size preferences in MediaWiki is being reduced to three standardized options—Small (180px), Regular (250px), and Large (400px), as part of ongoing efforts to improve performance and reduce strain on thumbnail services. As a result, existing preferences will be mapped to the nearest new size (for example, smaller selections like 120px or 150px will render at 180px, while larger ones like 300px or 360px will render at 400px). The preferences interface will soon be updated to reflect these changes, and users who wish to opt out or provide feedback can do so. [https://phabricator.wikimedia.org/T424909] * From now on, even when a permission expires automatically, users will receive an Echo notification similar to the standard notification for permission changes. There is a difference between this and [[m:Special:MyLanguage/Global reminder bot|Global reminder bot]] in that the latter reminds users a week ''before'' the rights are due to expire, so that they can renew the rights. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:32}} community-submitted {{PLURAL:32|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, the problem where the ULS language selector in [[m:Special:Translate|Special:Translate]] would scroll vertically when it shouldn't, has been resolved. Previously, when users opened the "Translate to English" dropdown and typed certain inputs, the dialog would scroll vertically by a few pixels even when there was enough space to display all results. The dropdown no longer shifts unnecessarily when filtering languages. [https://phabricator.wikimedia.org/T358864] * The [[m:Special:GlobalWatchlist|Global Watchlist]], which lets you view your watchlists from multiple wikis on a single page, continues to improve. For example, watchlists for Wikibase sites such as [[:d:|Wikidata]] now support [[mw:Special:MyLanguage/Extension:EntitySchema|EntitySchema]] elements for better tracking. The Live Updates mode now refreshes the special page every 60 seconds to comply with the updated [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|global API rate limits]] for improved real-time responsiveness. Additionally, a directionality bug that displayed links as "changes 3" instead of "3 changes" in mixed-direction lists has been fixed. [https://phabricator.wikimedia.org/T415450][https://phabricator.wikimedia.org/T424422][https://phabricator.wikimedia.org/T418091] '''Updates for technical contributors''' * The second phase of [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits|global API rate limits]] has been rolled out to reduce the [[diffblog:2026/03/26/quo-vadis-crawlers-progress-and-whats-next-on-safeguarding-our-infrastructure/|impact of AI crawlers]] and ensure fair, sustainable access to Wikimedia resources, prioritising human and mission-aligned traffic. [[mw:Special:MyLanguage/Wikimedia APIs/Rate limits#Limits|Limits]] have been shifted from per-hour to per-minute, producing smoother traffic patterns and more predictable API load. Community users are not expected to be affected, and no action is required. Early indications show some User-Agent-based requestors are adjusting behaviour, and around 64% of automated API traffic has been identified. Monitoring continues, and Wikimedia Enterprise remains available for commercial support. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.46/wmf.27|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/19|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W19"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೨:೧೩, ೫ ಮೇ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30498077 --> == <span lang="en" dir="ltr">Tech News: 2026-20</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W20"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/20|Translations]] are available. '''Weekly highlight''' * Community Tech has published [[m:Special:MyLanguage/Community Wishlist/How to write a good wish|new guidance]] explaining how wishes on Community Wishlist are triaged and prioritized. The documentation is intended to help contributors write stronger proposals by clarifying the factors that influence prioritization decisions. Beyond vote counts, the guidance highlights considerations such as potential impact on the community when determining which wishes move forward. '''Updates for editors''' * The Reader Growth team is launching an experiment to test a new [[mw:Special:MyLanguage/Readers/Reader_Growth/Share_Card|Share Card feature]] that allows readers to create visually engaging cards from Wikipedia articles or selected article sections and share them online, with each card linking back to the original article to help expand readership and article discovery. The mobile-only A/B test will be available to a portion of readers on Arabic, Chinese, French, Vietnamese, and English Wikipedia to better understand reading and sharing habits, and is scheduled to begin the week of May 18 and run for four weeks. * The Android and iOS Wikipedia apps recently released the [[mw:Special:MyLanguage/Wikimedia_Apps/Team/25th_Birthday_Reading_Challenge|25-day reading challenge]] into Beta, as part of efforts to drive reader engagement by encouraging users to complete reading milestones. To track their reading streak during the challenge, App users can add a widget featuring Baby Globe to their home screen. The challenge officially begins May 11. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:17}} community-submitted {{PLURAL:17|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where the global preference for enabling syntax highlighting in wikitext could unexpectedly disable itself after being turned on, has now been fixed. [https://phabricator.wikimedia.org/T425286] '''Updates for technical contributors''' * [[File:Octicons-tools.svg|12px|link=|alt=|Advanced item]] The ResourceLoader module <bdi lang="zxx" dir="ltr"><code><nowiki>mediawiki.ui.input</nowiki></code></bdi>, deprecated since [[m:Special:MyLanguage/Tech/News/2023/39|September 2023]], will be removed this week. There is a [[mw:Special:MyLanguage/Codex/Migrating_from_MediaWiki_UI|guide for migrating from MediaWiki UI to Codex]] for any tools that use it. [https://phabricator.wikimedia.org/T420125] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.2|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/20|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W20"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೦:೫೦, ೧೨ ಮೇ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30524429 --> == <span lang="en" dir="ltr">Tech News: 2026-21</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W21"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/21|Translations]] are available. '''Weekly highlight''' * The Abstract Wikipedia team has identified five potential pilot wikis to assess their interest in adopting abstract articles on their wikis. The pilots are Malayalam, Bengali, Dagbani, Arabic, and Indonesian Wikipedia. The feedback period will be open until May 22. If your community is interested in becoming a pilot, [[m:Talk:Abstract Wikipedia|let us know on Meta]]. '''Updates for editors''' * An experiment to show [[mw:Special:MyLanguage/Readers/Reader Experience/Reading lists|Reading Lists]] to logged-out readers on mobile web will launch on May 18 across German, Spanish, Italian, Portuguese, Polish, Dutch, Turkish, and Urdu Wikipedias, and will run for one month. The effort supports broader goals of helping readers save and organize articles for later reading, while encouraging habits that could lead to future Wikipedia contributions. * To support a bookmark button in the Reading List beta feature, the "Tools > Action" menu has been updated to display icons, including the watch star indicator that helps editors identify temporarily watched articles. The icons now also match those used on mobile, improving consistency across platforms. The change is currently limited to the actions menu and mainly affects editors with privileged user rights. [https://phabricator.wikimedia.org/T426008] * [[mw:Special:MyLanguage/VisualEditor/Suggestion Mode|Suggestion Mode]] was released as an [[w:en:A/B test|A/B test]] for newcomer editors on the mobile website at [[phab:T421189|~15 Wikipedias]]. The experiment will measure the impact that Suggestion Mode has on the proportion of newcomer mobile web edit sessions that result in constructive (un-reverted) article edits. The experiment will also evaluate the feature's impact on editor retention, and monitor changes in revert and block rates. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:27}} community-submitted {{PLURAL:27|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue in the Wikipedia Android app where images could sometimes fail to load after opening a recommended reading list notification, has now been fixed. [https://phabricator.wikimedia.org/T418231] '''Updates for technical contributors''' * The [[mw:Special:MyLanguage/Wikidata Platform|Wikidata Platform team]] has published its [[d:Special:MyLanguage/Wikidata:SPARQL query service/WDQS backend update/Backend Replacement|backend replacement recommendation]] and accompanying [[wikitech:Wikidata Query Service/WDQS Architecture re-design|technical architecture]] for the migration of the Wikidata Query Service (WDQS) away from Blazegraph. Feedback is invited until May 25th 2026, especially on potential gaps and impacts on advanced use cases. Wikidata community members and WDQS users are also encouraged to help identify high-impact tools and workflows that may need attention on [[d:Wikidata:SPARQL query service/WDQS backend update/High-Impact Use Cases|this page]]. Feedback can be shared on the [[d:Wikidata talk:SPARQL query service/WDQS backend update|Migration talk page]] or during the [[d:Special:MyLanguage/Wikidata:Blazegraph Migration Office Hours|next office hour]]. See the [[d:Special:MyLanguage/Wikidata:Wikidata Platform team/Newsletter|WDP team newsletter]] for more details. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.3|MediaWiki]] '''In depth''' * On English, French, Japanese, and a few other Wikipedias, there was a [[diffblog:2025/09/02/better-detecting-bots-and-replacing-our-captcha/|trial of hCaptcha]], a third-party bot detection service. The trial showed that hCaptcha effectively detects and deters some bad-faith automated activity, on its own and by giving [[w:en:Wikipedia:Village pump (technical)/Archive 225#Introducing SuggestedInvestigations|checkusers and stewards]] signals to look into. Because the results were positive, hCaptcha will be rolled out across all wikis over the next few weeks. [[mw:Special:MyLanguage/Product Safety and Integrity/Anti-abuse signals/hCaptcha|See the hCaptcha project page]] for technical information about the implementation and privacy protections. [[diffblog:2026/05/04/better-detecting-bots-and-replacing-our-captcha-part-2/|Learn more]]. * The latest Community Tech update is now available, with progress across several Community Wishlist initiatives, including Reading Lists expansion from the mobile app to the website, new language support for "Who Wrote That" and the Personal Dashboard, improvements to 3D rendering and Charts, and upcoming work on talk page sorting, audio playback, and editing workflows. The update also shares current priorities, wishlist status trends, and opportunities for community feedback on future focus areas and the Wikimedia Foundation’s 2026–2027 Annual Plan. [[m:Special:MyLanguage/Community Wishlist/Updates#May 13, 2026: Latest updates from the Community Tech team|Read the full newsletter for details]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/21|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W21"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೧:೫೧, ೧೯ ಮೇ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30539262 --> == <span lang="en" dir="ltr">Tech News: 2026-22</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W22"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/22|Translations]] are available. '''Weekly highlight''' * Following a [[mw:Special:MyLanguage/Contributors/Account Creation Experiments#LOWM|successful account creation experiment]], an improved logged-out edit warning message will be deployed to all Wikimedia wikis in the first week of June. The change will only affect logged-out users on mobile web who open an editing session. The updated experience is designed to encourage account creation more clearly, while still allowing users to edit with temporary accounts. Results from the experiment showed a significant increase in account creation, with a 27% relative lift among users shown the updated message. As expected, as more people funnel into account creation, temporary accounts decreased by a relative 16%. The experiment did not show any significant changes in constructive edit rates or other monitored contributor metrics. [https://phabricator.wikimedia.org/T424595] '''Updates for editors''' * For security reasons, members of certain user groups are [[m:Special:MyLanguage/Mandatory two-factor authentication for users with some extended rights|required to have two-factor authentication]] (2FA) enabled. Members of these groups will be unable to disable the last 2FA method on their account, and it will be impossible to add users without 2FA to these groups. Users will still be able to add new authentication methods or remove them, as long as at least one method is continuously enabled. In the next few weeks, users without 2FA will be removed from these groups. Notably, this applies to bureaucrats. See the linked tasks for deployment schedules. [https://phabricator.wikimedia.org/T423119][https://phabricator.wikimedia.org/T423120] * [[m:Special:MyLanguage/WMDE Technical Wishes|WMDE Technical Wishes]] will run an [[w:en:A/B testing|A/B test]] on [[:phab:T415904|10 wikis]], testing [[m:WMDE Technical Wishes/References/Reference Previews|potential improvements for Reference Previews]]. The experiment will run for ~2 weeks at the end of May / beginning of June and will affect 10% of desktop readers on the participating wikis. * After two successful experiments, the Reader Growth team is rolling out an [[mw:Special:MyLanguage/Readers/Reader Growth/Image Browsing|Image Browsing]] beta feature for all Wikipedias on mobile on May 25. This means that anyone who has all beta features on by default will start to see this feature, and others can check the box to turn it on in their preferences. The beta feature will include a carousel of all an article's images at the top of the article, with controls for editors to [[mw:Readers/Reader_Growth/Image_Browsing#Phase_2.1_beta_feature|exclude images from the article's carousel or to exclude an article from the feature entirely]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:30}} community-submitted {{PLURAL:30|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, three dimensional STL files were being rendered incorrectly by the media viewer 3D extension which is now fixed. [https://phabricator.wikimedia.org/T416723] '''Updates for technical contributors''' * The legacy CSS classes <bdi lang="zxx" dir="ltr"><code><nowiki>tleft</nowiki></code></bdi> and <bdi lang="zxx" dir="ltr"><code><nowiki>tright</nowiki></code></bdi> have been replaced with <bdi lang="zxx" dir="ltr"><code><nowiki>floatleft</nowiki></code></bdi> and <bdi lang="zxx" dir="ltr"><code><nowiki>floatright</nowiki></code></bdi> as the former do not work consistently across all MediaWiki platforms, notably mobile web and mobile apps. Projects relying on these classes are encouraged to review related usage and plan for migration. Please note that <bdi lang="zxx" dir="ltr"><code><nowiki>floatleft</nowiki></code></bdi> and <bdi lang="zxx" dir="ltr"><code><nowiki>floatright</nowiki></code></bdi> may also be deprecated in future, although there are currently no plans to do so. [[phab:T426452|Read more]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.4|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/22|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W22"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೩:೨೨, ೨೬ ಮೇ ೨೦೨೬ (IST) <!-- Message sent by User:Quiddity (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30584502 --> == <span lang="en" dir="ltr">Tech News: 2026-23</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W23"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/23|Translations]] are available. '''Updates for editors''' * The [[mw:Special:MyLanguage/Readers/Reader Experience|Reader Experience team]] is conducting an experiment to show the [[mw:Special:MyLanguage/Readers/Reader Experience/Reading lists|reading lists]] feature, which is still in development, to logged-out mobile readers to test whether it encourages account creation at a higher rate compared to the watchstar button. The [[mw:Special:MyLanguage/Readers/Reader Experience/Reading lists#Experiment timeline|experiment]] was launched on May 18th on German, Spanish, Italian, Portuguese, Polish, Dutch, Turkish, and Urdu wikis, and it will run for a month. * The Wikimedia Apps team released [[mw:Special:MyLanguage/Wikimedia Apps/Team/Explore Feed Refresh/Phase 1|Phase 1]] of the redesigned Home Feed to the Android Beta app. The new Home Feed includes a refreshed "Community" tab and a personalized "For You" tab featuring daily updated reading recommendations. The redesign is part of a broader effort to improve content discovery and create more engaging learning experiences in the Wikipedia apps. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:18}} community-submitted {{PLURAL:18|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where images could fail to load for some suggested edits on [[w:Special:Homepage|Special:Homepage]], leaving the thumbnail stuck in a loading state, has now been fixed. [https://phabricator.wikimedia.org/T424048] '''Updates for technical contributors''' * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.5|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/23|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W23"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೨:೩೮, ೨ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30613639 --> == <span lang="en" dir="ltr">Tech News: 2026-24</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W24"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/24|Translations]] are available. '''Weekly highlight''' * Wikimedia Enterprise has increased the free usage limits for its API offerings. The monthly request limit for the On-demand API has increased from 5,000 to 50,000 requests, while the Snapshot API limit has increased from 15 to 30 requests per month. In addition, Structured Contents snapshots are now available for free accounts. These changes expand access to Wikimedia Enterprise data for developers, researchers, and organizations using Wikimedia content. [https://enterprise.wikimedia.com/blog/enhanced-free-api] '''Updates for editors''' * The [[mw:Special:MyLanguage/Wikimedia_Apps/Team/Explore Feed Refresh/Phase 1|refreshed Explore Feed]], now called the Home Feed, is rolling out to 50% of users of the Wikipedia Android app. The Home Feed helps readers discover relevant content through two new tabs: ''Community'' and ''For You''. The Community tab provides a scrollable feed of curated content and updates from the broader Wikimedia community and movement, while the ''For You'' tab offers a full-screen, swipeable experience that shows content tailored to a user's interests. The redesign is part of a broader effort to improve discovery and enhance the learning experience in the Wikipedia app. * The [[mw:Special:MyLanguage/Wikimedia Apps/Team/iOS/"Which came first?" Game|Which came first?]] daily trivia game is now available in the beta version of the Wikipedia iOS app in English, German, French, Portuguese, Russian, Spanish, Arabic, Chinese, and Turkish. The game uses historical events from Wikipedia's "On This Day" content and challenges readers to guess which of two events happened first. The game was previously released on Android. Communities interested in making the game available in their languages can [[mw:Special:MyLanguage/Wikimedia_Apps/Team/Games#Game availability by language|read the instructions and requirements]]. * [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|Sub-referencing]], a new MediaWiki feature that allows editors to reuse references with different details, will begin rolling out to Wikimedia wikis following a successful pilot phase. Deployment will start on 8 June for most [[wikitech:Deployments/Train#Wednesday|Group 1 wikis]] and French Wikipedia, with additional Wikipedia language editions receiving the feature over the coming months. Communities are encouraged to prepare by checking for [https://translatewiki.net/w/i.php?title=Special%3ATranslate&group=ext-cite&language=en&action_source=search&filter=%21translated&optional=1&action=translate untranslated Cite extension messages] in their language and reviewing any use of [[mw:Special:MyLanguage/Reference Tooltips|Reference Tooltips]], which may require [[:phab:T416304#11668731|updates]] to support the new functionality. Wikis using [[mw:Special:MyLanguage/Help:Reference Previews|Reference Previews]] do not need to take any action. Communities may also wish to create the ''cite-tracking-category-ref-details'' [[Special:TrackingCategories|tracking category]] as a hidden category using <code><nowiki>__HIDDENCAT__</nowiki></code> (or a dedicated template), and connect it to the corresponding Wikidata item [[d:Q129764848]]. [https://phabricator.wikimedia.org/T425662] * The [[mw:Special:MyLanguage/Readers/Reader Growth/Mobile page previews#Experimentation|Page Previews experiment]] on mobile web has concluded. The team decided not to roll out the feature after the results showed no statistically significant impact on reader retention, as the primary success metric was retention improvement. Page Previews, which are already available on desktop and in the apps, display a thumbnail, lead paragraph, and link to the full article when readers tap a blue link. The experiment tested this experience on mobile web across six Wikipedias. * The [[mw:Special:MyLanguage/Codex/Design/Icons|user interface icon library]] will be [[phab:T399175|updated later this week or next week]]. Most of the ~300 icons have been slightly refined and ~30 new icons have been added. These changes improve the icons to make them more consistent and comprehensible, and provide more visual balance when they are used in groups. * The [[mw:Special:MyLanguage/Universal Language Selector|Universal Language Selector]] (ULS) interface in MediaWiki, which helps users select content in other languages, has been updated. The new version improves speed and accessibility, and users of Wikimedia projects can now pin languages for quicker language switching. The deployment to Wikimedia sites will happen gradually in the coming weeks. You can test it now as a beta feature by selecting [[Special:Preferences#mw-prefsection-betafeatures|beta features]] in your profile preferences and share your feedback on [[mw:Special:MyLanguage/Universal Language Selector/New ULS|the project page]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:21}} community-submitted {{PLURAL:21|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where the Pageviews Analysis dashboard on pageviews.wmcloud.org stopped updating graph data in May 2026, affecting all users, has been fixed. [https://phabricator.wikimedia.org/T427171] '''Updates for technical contributors''' * The function signature for <bdi lang="zxx" dir="ltr"><code><nowiki>mw.util.addPortletLink()</nowiki></code></bdi> has been simplified. Developers can now pass a configuration object instead of a list of positional parameters when creating portlet links. The previous function signature remains supported for backwards compatibility. For example, instead of: <bdi lang="zxx" dir="ltr"><code><nowiki>mw.util.addPortletLink('p-cactions', '#', 'Stub', 'ca-stubtag', 'Add a stub tag to this page');</nowiki></code></bdi> use <bdi lang="zxx" dir="ltr"><code><nowiki>mw.util.addPortletLink('p-cactions', { href: '#', text: 'Stub', id: 'ca-stubtag', tooltip: 'Add a stub tag to this page' });</nowiki></code></bdi>. Script maintainers are encouraged to review existing uses of <bdi lang="zxx" dir="ltr"><code><nowiki>addPortletLink()</nowiki></code></bdi> and update them where appropriate. This change will be available on all wikis from 11 June. Thanks to community volunteer Gerges for contributing this improvement. [https://phabricator.wikimedia.org/T427945] * '''Community Wishlist discussion''': Product & Technology [[m:Special:MyLanguage/Community Wishlist/Updates#May 20, 2026: Community Tech becomes a program|introduced changes]] meant to increase the number and complexity of wishes fulfilled, including the disbanding of the Community Tech team. They are [[m:Special:MyLanguage/Community Wishlist/Updates|engaging in discussions]] about a [[m:Talk:Community Wishlist#Proposed direction for Wishlist|proposed direction for the wishlist]] from community members. Includes ways to structure annual voting, better tracking of wishes, removing focus areas, and [[m:Special:MyLanguage/Community Wishlist/Updates|staffing updates]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.6|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/24|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W24"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೦೨:೫೯, ೯ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30650573 --> == <span lang="en" dir="ltr">Tech News: 2026-25</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W25"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/25|Translations]] are available. '''Weekly highlight''' * The [[mw:Special:MyLanguage/Readers/Reader Growth|Reader Growth team]] has launched an [[mw:Special:MyLanguage/Readers/Reader Growth/Image Browsing|Image Browsing]] beta feature on the mobile web version of all Wikipedias. The feature shows an image carousel at the top of articles with 3 or more images. Editors can configure this feature with the following controls: to hide a specific image from a page, either use <code>class=notpageimage</code> excluding it from thumbnail previews, or <code>class=noviewer</code> excluding it from MediaViewer. The carousel can also be disabled from a page entirely, with the magic word <code><nowiki>__NOMEDIAVIEWERCAROUSEL__</nowiki></code>. To submit feedback or flag bugs, please visit the [[mw:Talk:Readers/Reader Growth/Image Browsing|project page]]. * [[mw:Special:MyLanguage/Help:Tables#class="wikitable"|Wikitables]] can now be [[mw:Special:MyLanguage/Help:Sortable tables#Forcing the initial sort direction|sorted in descending order]] on the first click by adding <code dir=ltr>data-sort-order="desc"</code> to the header cell. Previously, by default, clicking a column header for the first time sorts it in ascending order. This addition to a Wikitable gives it more control and flexibility, while the default behavior for subsequent clicks remains unchanged. [https://phabricator.wikimedia.org/T398416] '''Updates for editors''' * The [[mw:Special:MyLanguage/Article guidance|Article guidance]] feature is currently being tested with some editors creating new articles on the Simple English, French, and Turkish Wikipedias. The experiment will soon begin on the Arabic and Bangla Wikipedias as well. [[w:simple:Special:NewArticle|This feature]] gives editors community-curated guidance to help them create articles that follow community standards. Experienced editors can continue creating or adapting outlines for specific article types that are commonly created by less experienced contributors. The outlines guide less experienced editors in creating high-quality articles. A quick guide to markups used in outlines can be found on [[mw:Special:MyLanguage/Article guidance/Test feature guide#Markups in outlines|this page]]. [[w:simple:Wikipedia:Article Guidance|Example outlines]] that can be adapted and instructions for how to adapt them are on [[mw:Special:MyLanguage/Article guidance#Adapting a sample outline in a Wikipedia|this section]] of the project page. * Wikis that wish to replace the "indefinitely" button in Special:Block for temporary accounts (for example, wikis that block temporary users only until account expiration) will be able to do so by creating [[MediaWiki:ipb-indefinite-expiry-temporary-account]] with the block duration they want. [https://phabricator.wikimedia.org/T427125] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:41}} community-submitted {{PLURAL:41|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. '''Updates for technical contributors''' * By the end of June, a valid user-agent string will be required for automated dumps downloads from the dumps.wikimedia.org website. Automated requests that provide a generic or empty user-agent will be blocked. This [[phab:T400119|extends enforcement]] of the long standing [[foundation:Special:MyLanguage/Policy:Wikimedia Foundation User-Agent Policy|user-agent policy]]. Access to dumps through Wikimedia Cloud Services will not change. * The roll out of global [[mw:Wikimedia APIs/Rate limits|API rate limits]] is now complete, with limits enforced across all APIs and at the documented levels for all groups. Bots running in Toolforge/WMCS or with the bot user right on any wiki remain exempt. All bots should continue to follow the documented best practices to avoid being rate limited. * The [https://api.wikimedia.org/wiki/Main_Page API Portal wiki] will be read only starting this week (June 15-18). The following week (June 22-25), all API Portal wiki URLs will redirect to [[mw:Wikimedia APIs|Wikimedia APIs on mediawiki.org]]. Learn more on the [[wikitech:API Portal/Deprecation|project page]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.7|MediaWiki]] '''Meetings and events''' * On June 17th at 6pm UTC the WMF will be holding Discord call focused on a code review. We've heard through the [[mw:Special:MyLanguage/Developer Satisfaction Survey/2026|Developer Satisfaction Survey]] that volunteers are struggling with code review and we'd like to discuss these experiences with the goal of surfacing workable solutions. You can join the call [https://discord.gg/wikipedia?event=1514727511102062664 via the Wikimedia Community Discord server]. * The [[m:Special:MyLanguage/Conferencia Wikimedia de América Latina 2026|Latin American Wikimedia Conference]] will host a regional hackathon that will bring together the Wikimedia movement’s technical community including developers, system administrators, data scientists, and users with extended rights. Interested technical contributors can [https://docs.google.com/forms/d/e/1FAIpQLSf4osJzTHBJjQbYJk7TMVEJjTEQv7IgtsUDfP-o-qTgeRQQxw/viewform apply for a scholarship] to participate until June 21 at midnight (Bolivia time, UTC-4). * Sign up for Wikimania Team Challenges to join this special event. The Team challenges will take place online and in person from July 21 to 22, before Wikimania conference. Everyone is welcome, regardless of skills or Wikimania registration. Teams will work on 10 important challenges supporting the Wikimedia community. For details, visit [[wmania:Special:MyLanguage/2026:Team challenges|the Team Challenges page]] and [https://wikimedia.eventyay.com/wm/teamchallenges/ register there]. Registration closes on June 20th at 11pm UTC. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/25|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W25"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೨:೧೮, ೧೫ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:UOzurumba (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30689604 --> == <span lang="en" dir="ltr">Tech News: 2026-26</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W26"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/26|Translations]] are available. '''Weekly highlight''' * [[mw:Special:MyLanguage/Growth/Feature summary|Growth features]] are [[phab:T418115|now available at Wikidata]]. This update enables access to Mentorship ([[mw:Special:MyLanguage/Help:Growth/Mentorship|if configured]]), Impact module, the Help Panel, and a simplified Newcomer Homepage (without Suggested Edits). Wikidata administrators are still configuring the features through Community Configuration. '''Updates for editors''' * The special page [[{{#special:RangeCalculator}}]] has been created. It allows users to find an IP range without needing to rely on external tools. Until now, this tool was only available to CheckUsers. [https://phabricator.wikimedia.org/T268429] * [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing|Sub-referencing]] is a new MediaWiki feature that allows editors to reuse references with different details. It will be deployed to most small and medium-sized Wikipedia language versions on June 23. The [[m:Special:MyLanguage/WMDE Technical Wishes/Sub-referencing#deployment|FAQ]] lists possible actions to take on your wiki to support the deployment. Check the [[:phab:T414094|rollout plan]] for the next deployment steps. [https://phabricator.wikimedia.org/T428902] * Starting next week, users will get a notification when they are blocked or unblocked from editing, or if this block changes. [https://phabricator.wikimedia.org/T100974] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:32}} community-submitted {{PLURAL:32|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. '''Updates for technical contributors''' * Starting next week, abuse filters that are set to "require CAPTCHA verification" will begin to also affect users with the <code>skipcaptcha</code> right, which includes most autoconfirmed users. Bots are exempted. This change only affects edits that trigger an abuse filter. The <code>skipcaptcha</code> right will continue to exempt users from having to solve CAPTCHAs in the ordinary course of using the wikis. [https://phabricator.wikimedia.org/T402595] * Reference documentation for the [[wikitech:Machine_Learning/LiftWing/API|Lift Wing API]] has moved from the API Portal to the interactive [https://wikitech.wikimedia.org/w/index.php?api=lift-wing&title=Special%3ARestSandbox REST Sandbox]. * The API Portal wiki is now closed. For API documentation, see [[mw:Special:MyLanguage/Wikimedia_APIs|Wikimedia APIs on mediawiki.org]]. All API Portal wiki URLs (https://api.wikimedia.org/wiki/) will redirect to the mediawiki.org page starting June 22. [https://phabricator.wikimedia.org/T427537] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.8|MediaWiki]] '''Meetings and events''' * Join an online call on 25 June at 2:30pm UTC to meet the current Wikimedia interns for [[mw:Google_Summer_of_Code/2026|Google Summer of Code]] and [[mw:Outreachy/Round_32|Outreachy]]. Interns will provide an overview of their projects and a brief demo of their work so far. Attendees are encouraged to [[mw:event:Google_Summer_of_Code/Summer_2026_June_Internship_open_session|share ideas and connections in their community]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/26|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W26"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೧೮:೩೫, ೨೩ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:Trizek (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30722494 --> == <span lang="en" dir="ltr">Tech News: 2026-27</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W27"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/27|Translations]] are available. '''Updates for editors''' * As part of the [[mw:Special:MyLanguage/Contributors/Account Creation Experiments|Account Creation Experiments]], the Growth team tested adding a user account icon in the mobile web header for logged-out users, providing direct access to "Create account" and "Log in" actions. The experiment increased account creation by about 20% without negatively affecting edit quality or constructive edit rates. The feature will now be rolled out to all Wikimedia Foundation wikis on mobile web in the first week of July. [https://phabricator.wikimedia.org/T428220] * After a [[phab:T426248|successful experiment]], logged-in users who did not [[mw:Special:MyLanguage/Help:Email_confirmation|confirm their email address]] when their account was created see a new banner asking them to complete that process. This helps reduce the risk that users get locked out of their account, and makes account email addresses overall more reliable. This is part of the [[mw:Special:MyLanguage/Product Safety and Integrity/Account Security|Account Security]] project. [https://phabricator.wikimedia.org/T428292] * An update to [[Special:Search|Search]] is refining how the <bdi lang="zxx" dir="ltr"><code><nowiki>-prefix:</nowiki></code></bdi> behaves when used to exclude results. Previously, using <bdi lang="zxx" dir="ltr"><code><nowiki>-prefix:</nowiki></code></bdi> with negation could unintentionally broaden search results by adding the namespaces included in the search scope, leading to confusing behavior for users expecting a straightforward exclusion filter. With the update, <bdi lang="zxx" dir="ltr"><code><nowiki>-prefix:</nowiki></code></bdi> will now strictly exclude matching page titles as intended and may display a warning if the relevant namespace has not been explicitly selected. The behavior of <bdi lang="zxx" dir="ltr"><code><nowiki>prefix:</nowiki></code></bdi> without negation however remains unchanged. [https://phabricator.wikimedia.org/T427443] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:33}} community-submitted {{PLURAL:33|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where reviewers using the Page Curation toolbar were not automatically subscribed to talk page discussions they started has now been fixed. Reviewers will now receive notifications when someone replies to those discussions. [https://phabricator.wikimedia.org/T329346] '''Updates for technical contributors''' * Starting June 29th, automated downloads from the dumps.wikimedia.org website will be subject to the [[Foundation:Special:MyLanguage/Policy:Wikimedia Foundation User-Agent Policy|user-agent policy]]. Automated requests that provide a generic or empty user-agent will be blocked. Access to dumps through Wikimedia Cloud Services remains unaffected. This is a follow up to the announcement made in the [[m:Special:MyLanguage/Tech/News/2026/25|2026/25 issue of Tech News]]. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.9|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/27|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W27"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೧೭:೧೭, ೨೯ ಜೂನ್ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30744833 --> == <span lang="en" dir="ltr">Tech News: 2026-28</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W28"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/28|Translations]] are available. '''Updates for editors''' * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:34}} community-submitted {{PLURAL:34|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where the search bar results on Wikidata, showed English results instead of using the correct language fallback for users of language variants, has now been fixed. Search suggestions will now follow the expected language fallback chain. [https://phabricator.wikimedia.org/T429769] '''Updates for technical contributors''' * In preparation for [[m:Special:MyLanguage/Event:Celebrate Women|Celebrate Women campaign]] planned for March 2027, the Wikimedia Foundation’s [[m:Special:MyLanguage/Wikimedia Foundation/Advancement/Community Growth/Content Enablement|Content Enablement team]] has launched a 22-question survey to better understand technical contributions by women+ (anyone who identifies as a woman) across Wikimedia projects. The survey takes approximately 15–20 minutes to complete and will remain open until 20 July 2026. The [[m:Special:MyLanguage/Celebrate Women/Technical contributions survey|questions]] are also available on-wiki for review in advance. * The [[mw:Special:MyLanguage/Extension:Score|Score extension]] now supports rendering music scores as SVG images in addition to PNG, addressing a long-standing [[:phab:T49578|feature request]] and resolving historical image quality issues. Both formats are now provided to clients, with PNG in the <bdi lang="zxx" dir="ltr"><code><nowiki>src</nowiki></code></bdi> attribute and SVG in the <bdi lang="zxx" dir="ltr"><code><nowiki>srcset</nowiki></code></bdi> attribute. * The new [[wikitech:Parsoid|Parsoid]] parser [[mw:Special:MyLanguage/Parsoid/Parser_Unification/Updates|continues to be deployed to additional wikis]], making it easier to introduce new reading and editing features. It was enabled on French Wikipedia, bringing total progress to covering 78.9% of Wikipedia page views. Rollout to English Wikipedia desktop will progress through this week. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.10|MediaWiki]] '''In depth''' * The Wikimedia Hackathon 2026 [[diffblog:2026/06/29/wikimedia-hackathon-2026-building-collaborating-and-shaping-the-future-together/|recap blog post]] is now live. It highlights the projects, sessions, and social activities from this year’s event, and shares initial plans for the 2027 Wikimedia Hackathon. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/28|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W28"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೧೯:೨೬, ೬ ಜುಲಾಯಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30773578 --> == <span lang="en" dir="ltr">Tech News: 2026-29</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W29"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/29|Translations]] are available. '''Updates for editors''' * [[mw:Special:MyLanguage/Growth/Revise_Tone|Revise Tone]] helps newcomers identify passages in Wikipedia articles that may contain non-encyclopedic language and encourages them to consider revising the tone. The feature was [[w:en:A/B_testing|A/B tested]] on the Arabic, English, French, and Portuguese Wikipedias, where newcomer task completion rates [[mw:Special:MyLanguage/Growth/Revise_Tone#Experiment_Results|increased by 38.7%]] compared to the default Copyedit task, with no decrease in edit quality. The test ended on July 9, and the feature is now available for everyone on these wikis, configurable via Community Configuration. [[phab:T426364|The plan]] is to release Revise Tone to more wikis. * The community configuration that allows [[mw:Special:MyLanguage/Help:Growth/Mentorship#Automated mentor list cleanup|automatic removal of inactive mentors]] based on configurable criteria will be enabled on Thursday 16, [[mw:Special:MyLanguage/Growth/Deployment|on some wikis]] to keep mentor lists up to date. Mentors are experienced contributors who opt in to help new users on-wiki through the [[mw:Special:MyLanguage/Growth/Feature summary|Growth Features]]. Administrators can now prepare the settings via [[w:Special:CommunityConfiguration/Mentorship|Special:CommunityConfiguration/Mentorship]]; they will take effect starting Thursday. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:38}} community-submitted {{PLURAL:38|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where some users of the Wikipedia Android app were logged out immediately after signing in, preventing them from staying logged in and editing pages, has now been fixed. [https://phabricator.wikimedia.org/T316916] '''Updates for technical contributors''' * Editing a page via user scripts or gadgets was causing watchlist labels that the user had assigned to that page to reset. This has now been fixed. [https://phabricator.wikimedia.org/T423778] * To work around a Safari bug (see [[phab:T425211]]), on Parsoid-enabled wikis, wikilink hrefs now use absolute urls instead of protocol-relative urls. REST API output remains unchanged and continue to use protocol-relative urls. Gadgets, user scripts, bots, and CSS might need to be adapted if they relied on the presence of protocol-relative urls in wikilink hrefs. [https://phabricator.wikimedia.org/T431358] * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.11|MediaWiki]] '''In depth''' * The Wikimedia Foundation’s Experiment Platform Team has published a blog post reflecting on its first year of structured experimentation. It highlights successful experiments such as Paste Check, Reference Check, and Tone Check, which improved editing outcomes and have been rolled out to more users, as well as experiments that did not lead to product changes. [[diffblog:2026/07/07/moving-the-needle-how-we-test-new-ideas-across-wikimedia-projects|Read more]]. '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/29|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W29"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೨೧:೪೧, ೧೩ ಜುಲಾಯಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30804401 --> == <span lang="en" dir="ltr">Tech News: 2026-30</span> == <div lang="en" dir="ltr"> <section begin="technews-2026-W30"/><div class="plainlinks"> Latest '''[[m:Special:MyLanguage/Tech/News|tech news]]''' from the Wikimedia technical community. Please tell other users about these changes. Not all changes will affect you. [[m:Special:MyLanguage/Tech/News/2026/30|Translations]] are available. '''Updates for editors''' * The [[mw:Special:MyLanguage/Reader/Reader Experience|Reader Experience team]] has incorporated community feedback around the placement of watchstar and watchlist buttons for the [[mw:Special:MyLanguage/Readers/Reader Experience/WE3.3.4 Reading lists|Reading Lists beta feature]], which would allow for saving articles for later reading – a [[m:Special:MyLanguage/Community Wishlist/W102|wishlist item]] to bring the functionality to web. Editors are invited to enable the beta feature to test it out and [[phab:T426453|share their thoughts]]. * [[mw:Special:MyLanguage/VisualEditor/Suggestion Mode|Suggestion Mode]] offers edit suggestions within the VisualEditor for improving Wikipedia articles. [[Special:EditChecks|All suggestions]] are [[mw:Special:MyLanguage/Help:Suggestion mode#For administrators – local customization|community-configurable]]. The [[mw:Special:MyLanguage/Edit check/TextMatch|TextMatch]] feature is a way for volunteers to create custom local suggestions. The feature searches in articles for strings of text, and now includes support for regular expressions. This gives volunteers greater precision and flexibility over the kinds of local suggestions they can create. Note: Suggestions [[mw:Special:MyLanguage/Edit check/Configuration#:~:text=maximumEditcount|can be targeted]] based on the edit count of the person editing as well as other aspects of the page. You can [[mw:Special:MyLanguage/Help:Suggestion mode#Create custom local types of Suggestions|find examples from other communities]] for inspiration, including TextMatches that detect: typos, grammar-errors, potential advertisements, clichés, incorrect dash or hyphen usage, non-specific time keywords, outdated names, and more. Any [[mw:Special:MyLanguage/Talk:VisualEditor/Suggestion Mode|feedback]] is appreciated. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] View all {{formatnum:27}} community-submitted {{PLURAL:27|task|tasks}} that were [[m:Special:MyLanguage/Tech/News/Recently resolved community tasks|resolved last week]]. For example, an issue where the SVG Translate tool could use an outdated version of a file, causing existing translations to be overwritten when new ones were uploaded, has now [[phab:T430577|been fixed]]. Overall, in the last quarter from April – June 2026 about 337 community tasks were resolved by the Wikimedia Foundation. '''Updates for technical contributors''' * On Parsoid-enabled wikis, Parsoid now renders a maximum of 1,250 images per page. A new tracking category, "media-limit-reached", will be soon made available to identify pages where this limit is reached, making it easier to find content whose media output may have been restricted during rendering. See [[phab:T430854]] for more information and to provide feedback. * [[File:Reload icon with two arrows.svg|12px|link=|class=skin-invert|Recurrent item]] Detailed code updates later this week: [[mw:MediaWiki 1.47/wmf.12|MediaWiki]] '''''[[m:Special:MyLanguage/Tech/News|Tech news]]''' prepared by [[m:Special:MyLanguage/Tech/News/Writers|Tech News writers]] and posted by [[m:Special:MyLanguage/User:MediaWiki message delivery|bot]]&nbsp;• [[m:Special:MyLanguage/Tech/News#contribute|Contribute]]&nbsp;• [[m:Special:MyLanguage/Tech/News/2026/30|Translate]]&nbsp;• [[m:Tech|Get help]]&nbsp;• [[m:Talk:Tech/News|Give feedback]]&nbsp;• [[m:Global message delivery/Targets/Tech ambassadors|Subscribe or unsubscribe]].'' </div><section end="technews-2026-W30"/> </div> <bdi lang="en" dir="ltr">[[User:MediaWiki message delivery|MediaWiki message delivery]]</bdi> ೧೧:೧೬, ೨೧ ಜುಲಾಯಿ ೨೦೨೬ (IST) <!-- Message sent by User:STei (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Tech_ambassadors&oldid=30836091 --> 2l3q9cpb5smoy6txhle61nsrg858xsi ಮಾದ್ಯಮೊವಿಕಿ:Gadget-twinklearv.js 8 25380 365982 355490 2026-06-24T06:31:17Z en>Novem Linguae 0 Repo at f08cefa: add rules for no space inside parentheses (#2341) 365982 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinklearv.js: ARV module **************************************** * Mode of invocation: Tab ("ARV") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.arv = function twinklearv() { const username = mw.config.get('wgRelevantUserName'); if (!username || username === mw.config.get('wgUserName')) { return; } const isIP = mw.util.isIPAddress(username, true); // Ignore ranges wider than the CIDR limit if (Morebits.ip.isRange(username) && !Morebits.ip.validCIDR(username)) { return; } const userType = isIP ? 'IP' + (Morebits.ip.isRange(username) ? ' range' : '') : 'user'; Twinkle.addPortletLink(() => { Twinkle.arv.callback(username, isIP); }, 'ARV', 'tw-arv', 'Report ' + userType + ' to administrators'); }; Twinkle.arv.callback = function(uid, isIP) { const Window = new Morebits.SimpleWindow(600, 500); Window.setTitle('Advance Reporting and Vetting'); // Backronym Window.setScriptName('Twinkle'); Window.addFooterLink('AIV guide', 'WP:GAIV'); Window.addFooterLink('UAA guide', 'WP:UAAI'); Window.addFooterLink('SPI guide', 'Wikipedia:Sockpuppet investigations/SPI/Guide to filing cases'); Window.addFooterLink('ARV prefs', 'WP:TW/PREF#arv'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#arv'); Window.addFooterLink('Give feedback', 'WT:TW'); const form = new Morebits.QuickForm(Twinkle.arv.callback.evaluate); form.append({ type: 'select', name: 'category', label: 'Select report type:', event: Twinkle.arv.callback.changeCategory, list: [ { label: 'Vandalism (WP:AIV)', value: 'aiv' }, { label: 'Username (WP:UAA)', value: 'username', disabled: isIP }, { label: 'Sockpuppeteer (WP:SPI)', value: 'sock' }, { label: 'Sockpuppet (WP:SPI)', value: 'puppet' }, { label: 'Edit warring (WP:AN3)', value: 'an3', disabled: Morebits.ip.isRange(uid) // rvuser template doesn't support ranges } ] }); form.append({ type: 'div', label: '', style: 'color: red', id: 'twinkle-arv-blockwarning' }); // Temporary account notice if (mw.config.get('wgRelevantUserName') && mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')) && !mw.config.get('wgCheckUserTemporaryAccountIPRevealAllowed')) { const temporaryAccountNotice = form.append({ type: 'field', label: 'Temporary account notice', name: 'ta_notice', style: 'color: var(--morebits-color-warning)' }); temporaryAccountNotice.append({ type: 'div', label: 'You are reporting a [[Wikipedia:Temporary accounts|temporary account]]. These accounts, with usernames like ~2025-12345-67, have replaced IP addresses. Please be careful when reporting for sockpuppetry and when blocking, because other users and temporary accounts on the same IP may end up as collateral damage.' }); } form.append({ type: 'field', label: 'Work area', name: 'work_area' }); form.append({ type: 'submit' }); form.append({ type: 'hidden', name: 'uid', value: uid }); const result = form.render(); Window.setContent(result); Window.display(); // Check if the user is blocked, update notice const query = { action: 'query', list: 'blocks', bkprop: 'range|flags', format: 'json' }; if (isIP) { query.bkip = uid; } else { query.bkusers = uid; } new Morebits.wiki.Api("Checking the user's block status", query, ((apiobj) => { const blocklist = apiobj.getResponse().query.blocks; if (blocklist.length) { // If an IP is blocked *and* rangeblocked, only use whichever is more recent const block = blocklist[0]; let message = (isIP ? 'This IP ' + (Morebits.ip.isRange(uid) ? 'range' : 'address') : 'This account') + ' is ' + (block.partial ? 'partially' : 'already') + ' blocked'; // Start and end differ, range blocked message += block.rangestart !== block.rangeend ? ' as part of a rangeblock.' : '.'; if (block.partial) { $('#twinkle-arv-blockwarning').css('color', 'black'); // Less severe } $('#twinkle-arv-blockwarning').text(message); } })).post(); // We must init the const evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.category.dispatchEvent(evt); }; Twinkle.arv.callback.changeCategory = function(e) { const value = e.target.value; const root = e.target.form; const old_area = Morebits.QuickForm.getElements(root, 'work_area')[0]; let work_area = null; switch (value) { case 'aiv': /* falls through */ default: work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report user for vandalism', name: 'work_area' }); work_area.append({ type: 'input', name: 'page', label: 'Primary linked page:', tooltip: 'Leave blank to not link to the page in the report', value: Twinkle.getPrefill('vanarticle') || '', event: function(e) { const value = e.target.value; const root = e.target.form; if (value === '') { root.badid.disabled = root.goodid.disabled = true; } else { root.badid.disabled = false; root.goodid.disabled = root.badid.value === ''; } } }); work_area.append({ type: 'input', name: 'badid', label: 'Revision ID for target page when vandalised:', tooltip: 'Leave blank for no diff link', value: Twinkle.getPrefill('vanarticlerevid') || '', disabled: !Twinkle.getPrefill('vanarticle'), event: function(e) { const value = e.target.value; const root = e.target.form; root.goodid.disabled = value === ''; } }); work_area.append({ type: 'input', name: 'goodid', label: 'Last good revision ID before vandalism of target page:', tooltip: 'Leave blank for diff link to previous revision', value: Twinkle.getPrefill('vanarticlegoodrevid') || '', disabled: !Twinkle.getPrefill('vanarticle') || Twinkle.getPrefill('vanarticlerevid') }); work_area.append({ type: 'checkbox', name: 'arvtype', list: [ { label: 'Vandalism after final (level 4 or 4im) warning given', value: 'final' }, { label: 'Vandalism after recent (within 1 day) release of block', value: 'postblock' }, { label: 'Evidently a vandalism-only account', value: 'vandalonly', disabled: mw.util.isIPAddress(root.uid.value, true) }, { label: 'Account is a promotion-only account', value: 'promoonly', disabled: mw.util.isIPAddress(root.uid.value, true) }, { label: 'Account is evidently a spambot or a compromised account', value: 'spambot' } ] }); work_area.append({ type: 'textarea', name: 'reason', label: 'Comment:' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'username': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report username violation', name: 'work_area' }); work_area.append({ type: 'header', label: 'Type(s) of inappropriate username', tooltip: 'Wikipedia does not allow usernames that are misleading, promotional, offensive or disruptive. Domain names and email addresses are likewise prohibited. These criteria apply to both usernames and signatures. Usernames that are inappropriate in another language, or that represent an inappropriate name with misspellings and substitutions, or do so indirectly or by implication, are still considered inappropriate.' }); work_area.append({ type: 'checkbox', name: 'arvtype', list: [ { label: 'Misleading username', value: 'misleading', tooltip: 'Misleading usernames imply relevant, misleading things about the contributor. For example, misleading points of fact, an impression of undue authority, or usernames giving the impression of a bot account.' }, { label: 'Promotional username', value: 'promotional', tooltip: 'Promotional usernames are advertisements for a company, website or group. Please do not report these names to UAA unless the user has also made promotional edits related to the name.' }, { label: 'Offensive username', value: 'offensive', tooltip: 'Offensive usernames make harmonious editing difficult or impossible.' }, { label: 'Disruptive username', value: 'disruptive', tooltip: 'Disruptive usernames include outright trolling or personal attacks, or otherwise show a clear intent to disrupt Wikipedia.' } ] }); work_area.append({ type: 'textarea', name: 'reason', label: 'Comment:' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'puppet': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report suspected sockpuppet', name: 'work_area' }); work_area.append( { type: 'input', name: 'sockmaster', label: 'Sockpuppeteer', tooltip: 'The username of the sockpuppeteer (sockmaster) without the "User:" prefix' } ); work_area.append({ type: 'textarea', label: 'Evidence:', name: 'evidence', tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'sock': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report suspected sockpuppeteer', name: 'work_area' }); work_area.append( { type: 'dyninput', name: 'sockpuppets', label: 'Sockpuppets', sublabel: 'Sock:', tooltip: 'The username of the sockpuppet without the "User:" prefix', min: 2 }); work_area.append({ type: 'textarea', label: 'Evidence:', name: 'evidence', tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'an3': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report edit warring', name: 'work_area' }); work_area.append({ type: 'input', name: 'page', label: 'Page', tooltip: 'The page being reported' }); work_area.append({ type: 'button', name: 'load', label: 'Load', event: function(e) { const root = e.target.form; const date = new Morebits.Date().subtract(48, 'hours'); // all since 48 hours // Run for each AN3 field const getAN3Entries = function(field, rvuser, titles) { const $field = $(root).find('[name=' + field + ']'); $field.find('.entry').remove(); new mw.Api().get({ action: 'query', prop: 'revisions', format: 'json', rvprop: 'sha1|ids|timestamp|parsedcomment|comment', rvlimit: 500, // intentionally limited rvend: date.toISOString(), rvuser: rvuser, indexpageids: true, titles: titles }).done((data) => { const pageid = data.query.pageids[0]; const page = data.query.pages[pageid]; if (!page.revisions) { $('<span>') .addClass('entry') .html('None found') .appendTo($field); } else { for (let i = 0; i < page.revisions.length; ++i) { const rev = page.revisions[i]; const $entry = $('<div>') .addClass('entry'); const $input = $('<input>') .attr({ type: 'checkbox', name: 's_' + field, value: JSON.stringify(rev) }); $input.appendTo($entry); let comment = '<span>'; // revdel/os if (typeof rev.commenthidden === 'string') { comment += '(comment hidden)'; } else { comment += '"' + rev.parsedcomment + '"'; } comment += ' at <a href="' + mw.config.get('wgScript') + '?diff=' + rev.revid + '">' + new Morebits.Date(rev.timestamp).calendar() + '</a></span>'; $entry.append(comment).appendTo($field); } } // add free form input for resolves if (field === 'resolves') { const $free_entry = $('<div>') .addClass('entry'); const $free_input = $('<input>') .attr({ type: 'text', name: 's_resolves_free' }); const $free_label = $('<label>') .attr('for', 's_resolves_free') .html('URL link of diff with additional discussions: '); $free_entry.append($free_label).append($free_input).appendTo($field); } }).fail(() => { $('<span>') .addClass('entry') .html('API failure, reload page and try again') .appendTo($field); }); }; // warnings const uid = root.uid.value; getAN3Entries('warnings', mw.config.get('wgUserName'), 'User talk:' + uid); // diffs and resolves require a valid page const page = root.page.value; if (page) { // diffs getAN3Entries('diffs', uid, page); // resolutions const t = new mw.Title(page); const talk_page = t.getTalkPage().getPrefixedText(); getAN3Entries('resolves', mw.config.get('wgUserName'), talk_page); } else { $(root).find('[name=diffs]').find('.entry').remove(); $(root).find('[name=resolves]').find('.entry').remove(); } } }); work_area.append({ type: 'field', name: 'diffs', label: 'User\'s reverts (within last 48 hours)', tooltip: 'Select the edits you believe are reverts' }); work_area.append({ type: 'field', name: 'warnings', label: 'Warnings given to subject', tooltip: 'You must have warned the subject before reporting' }); work_area.append({ type: 'field', name: 'resolves', label: 'Resolution initiatives', tooltip: 'You should have tried to resolve the issue on the talk page first' }); work_area.append({ type: 'textarea', label: 'Comment:', name: 'comment' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; } }; Twinkle.arv.callback.evaluate = function(e) { const form = e.target; let reason = ''; const input = Morebits.QuickForm.getInputData(form); switch (input.category) { // Report user for vandalism case 'aiv': /* falls through */ default: reason = Twinkle.arv.callback.getAivReasonWikitext(input); if (reason === null) { alert('You must specify some reason'); return; } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.actionCompleted.redirect = 'Wikipedia:Administrator intervention against vandalism'; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; var aivPage = new Morebits.wiki.Page('Wikipedia:Administrator intervention against vandalism', 'Processing AIV request'); aivPage.setPageSection(1); aivPage.setFollowRedirect(true); aivPage.load(() => { const text = aivPage.getPageText(); const $aivLink = '<a target="_blank" href="/wiki/WP:AIV">WP:AIV</a>'; // check if user has already been reported if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(input.uid) + '\\s*\\}\\}').test(text)) { aivPage.getStatusElement().error('Report already present, will not add a new one'); Morebits.Status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at ' + $aivLink + ':'); return; } // then check for any bot reports const tb2Page = new Morebits.wiki.Page('Wikipedia:Administrator intervention against vandalism/TB2', 'Checking bot reports'); tb2Page.load(() => { const tb2Text = tb2Page.getPageText(); const tb2statelem = tb2Page.getStatusElement(); if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(input.uid) + '\\s*\\}\\}').test(tb2Text)) { if (confirm('The user ' + input.uid + ' has already been reported by a bot. Do you wish to make the report anyway?')) { tb2statelem.info('Proceeded despite bot report'); } else { tb2statelem.error('Report from a bot is already present, stopping'); Morebits.Status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them at ' + $aivLink + ':'); return; } } else { tb2statelem.info('No conflicting bot reports'); } aivPage.getStatusElement().status('Adding new report...'); aivPage.setEditSummary('Reporting [[Special:Contributions/' + input.uid + '|' + input.uid + ']].'); aivPage.setChangeTags(Twinkle.changeTags); aivPage.setAppendText(Twinkle.arv.callback.buildAivReport(input)); aivPage.setDiscussionToolsAutoSubscribe(false); aivPage.append(); }); }); break; // Report inappropriate username case 'username': var censorUsername = input.arvtype.includes('offensive'); // check if the username is marked offensive reason = Twinkle.arv.callback.getUsernameReportWikitext(input); Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.actionCompleted.redirect = 'Wikipedia:Usernames for administrator attention'; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; var uaaPage = new Morebits.wiki.Page('Wikipedia:Usernames for administrator attention', 'Processing UAA request'); uaaPage.setFollowRedirect(true); uaaPage.load(() => { const text = uaaPage.getPageText(); // check if user has already been reported if (new RegExp('\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?' + Morebits.string.escapeRegExp(input.uid) + '\\s*(\\||\\})').test(text)) { uaaPage.getStatusElement().error('User is already listed.'); const $uaaLink = '<a target="_blank" href="/wiki/WP:UAA">WP:UAA</a>'; Morebits.Status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at ' + $uaaLink + ':'); return; } uaaPage.getStatusElement().status('Adding new report...'); uaaPage.setEditSummary('Reporting ' + (censorUsername ? 'an offensive username.' : '[[Special:Contributions/' + input.uid + '|' + input.uid + ']].')); uaaPage.setChangeTags(Twinkle.changeTags); // Blank newline per [[Special:Permalink/996949310#Spacing]]; see also [[WP:LISTGAP]] and [[WP:INDENTGAP]] uaaPage.setPageText(text + '\n' + reason + '\n*'); uaaPage.setDiscussionToolsAutoSubscribe(false); uaaPage.save(); }); break; // WP:SPI case 'sock': /* falls through */ case 'puppet': var reportData = Twinkle.arv.callback.getSpiReportData(input); if (reportData.error) { alert(reportData.error); return; } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" var reportpage = 'Wikipedia:Sockpuppet investigations/' + reportData.sockmaster; Morebits.wiki.actionCompleted.redirect = reportpage; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; var spiPage = new Morebits.wiki.Page(reportpage, 'Retrieving discussion page'); spiPage.setFollowRedirect(true); spiPage.setEditSummary('Adding new report for [[Special:Contributions/' + reportData.sockmaster + '|' + reportData.sockmaster + ']].'); spiPage.setChangeTags(Twinkle.changeTags); spiPage.setAppendText(reportData.wikitext); spiPage.setWatchlist(Twinkle.getPref('spiWatchReport')); spiPage.setDiscussionToolsAutoSubscribe(false); spiPage.append(); Morebits.wiki.removeCheckpoint(); // all page updates have been started break; case 'an3': // prepare the AN3 report, then post and notify Twinkle.arv.callback.getAn3ReportData(input).then((data) => { // If there are any reasons why the user might want to cancel the report, check with them about each reason and cancel if they choose to for (const confirmation of data.confirmations) { if (!confirm(confirmation)) { return; } } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" const reportpage = 'Wikipedia:Administrators\' noticeboard/Edit warring'; Morebits.wiki.actionCompleted.redirect = reportpage; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; const an3Page = new Morebits.wiki.Page(reportpage, 'Retrieving discussion page'); an3Page.setFollowRedirect(true); an3Page.setEditSummary('Adding new report for [[Special:Contributions/' + data.uid + '|' + data.uid + ']].'); an3Page.setChangeTags(Twinkle.changeTags); an3Page.setAppendText(data.reportWikitext); an3Page.setDiscussionToolsAutoSubscribe(false); an3Page.append(); // notify user const notifyText = '\n\n{{subst:an3-notice|1=' + mw.util.wikiUrlencode(data.uid) + '|auto=1}} ~~~~'; const talkPage = new Morebits.wiki.Page('User talk:' + data.uid, 'Notifying edit warrior'); talkPage.setFollowRedirect(true); talkPage.setEditSummary('Notifying about edit warring noticeboard discussion.'); talkPage.setChangeTags(Twinkle.changeTags); talkPage.setAppendText(notifyText); talkPage.append(); Morebits.wiki.removeCheckpoint(); // all page updates have been started }).catch((error) => { console.error('Error occurred while preparing AN3 report.', error); // eslint-disable-line no-console alert('Error occurred while preparing AN3 report: ' + error.message); }); break; } }; Twinkle.arv.callback.getAivReasonWikitext = function(input) { let text = ''; let type = input.arvtype; if (!type.length && input.reason === '') { return null; } type = type.map((v) => { switch (v) { case 'final': return 'vandalism after final warning'; case 'postblock': return 'vandalism after recent release of block'; case 'vandalonly': return 'actions evidently indicate a vandalism-only account'; case 'promoonly': return 'account is being used only for promotional purposes'; case 'spambot': return 'account is evidently a spambot or a compromised account'; default: return 'unknown reason'; } }).join('; '); if (input.page !== '') { // Allow links to redirects, files, and categories text = 'On {{No redirect|' + input.page + '}}'; if (input.badid !== '') { text += ' ({{diff|' + input.page + '|' + input.badid + '|' + input.goodid + '|diff}})'; } text += ':'; } if (type) { text += ' ' + type; } if (input.reason !== '') { const textEndsInPunctuationOrBlank = /([.?!;:]|^)$/.test(text); text += textEndsInPunctuationOrBlank ? '' : '.'; const textIsBlank = text === ''; text += textIsBlank ? '' : ' '; text += input.reason; } text = text.trim(); const textEndsInPunctuation = /[.?!;]$/.test(text); if (!textEndsInPunctuation) { text += '.'; } text += ' ~~~~'; text = text.replace(/\r?\n/g, '\n*:'); // indent newlines return text; }; Twinkle.arv.callback.buildAivReport = function(input) { return '\n*{{vandal|' + (/=/.test(input.uid) ? '1=' : '') + input.uid + '}} &ndash; ' + Twinkle.arv.callback.getAivReasonWikitext(input); }; Twinkle.arv.callback.getUsernameReportWikitext = function(input) { // generate human-readable string, e.g. "misleading and promotional username" if (input.arvtype.length <= 2) { input.arvtype = input.arvtype.join(' and '); } else { input.arvtype = [input.arvtype.slice(0, -1).join(', '), input.arvtype.slice(-1)].join(' and '); } // a or an? let adjective = 'a'; if (/[aeiouwyh]/.test(input.arvtype[0] || '')) { // non 100% correct, but whatever, including 'h' for Cockney adjective = 'an'; } let text = '*{{user-uaa|1=' + input.uid + '}} &ndash; '; if (input.arvtype.length) { text += 'Violation of the username policy as ' + adjective + ' ' + input.arvtype + ' username. '; } if (input.reason !== '') { text += Morebits.string.toUpperCaseFirstChar(input.reason); const endsInPeriod = /\.$/.test(input.reason); if (!endsInPeriod) { text += '.'; } text += ' '; } text += '~~~~'; text = text.replace(/\r?\n/g, '\n*:'); // indent newlines return text; }; Twinkle.arv.callback.getSpiReportData = function(input) { const isPuppetReport = input.category === 'puppet'; if (!isPuppetReport) { input.sockpuppets = input.sockpuppets.filter((sock) => sock !== ''); // ignore empty sockpuppet inputs } if (isPuppetReport && !input.sockmaster) { return { error: 'You have not entered a sockmaster account for this puppet. Consider reporting this account as a sockpuppeteer instead.' }; } else if (!isPuppetReport && input.sockpuppets.length === 0) { return { error: 'You have not entered any sockpuppet account(s) for this sockmaster. Consider reporting this account as a sockpuppet instead.' }; } input.sockmaster = input.sockmaster || input.uid; input.sockpuppets = isPuppetReport ? [input.uid] : Morebits.array.uniq(input.sockpuppets); let text = '\n{{subst:SPI report|' + input.sockpuppets.map((sock, index) => (index + 1) + '=' + sock).join('|') + '\n|evidence=' + Morebits.string.formatReasonText(input.evidence) + ' \n'; text += '}}'; return { sockmaster: input.sockmaster, wikitext: text }; }; Twinkle.arv.callback.getAn3ReportData = async function(input) { const confirmations = []; const diffs = input.s_diffs ? input.s_diffs.map(JSON.parse) : []; if (diffs.length < 3) { confirmations.push('You have selected fewer than three offending edits. Do you wish to make the report anyway?'); } const warnings = input.s_warnings ? input.s_warnings.map(JSON.parse) : []; if (!warnings.length) { confirmations.push('You have not selected any edits where you warned the offender. Do you wish to make the report anyway?'); } const resolves = input.s_resolves ? input.s_resolves.map(JSON.parse) : []; const freeResolves = input.s_resolves_free; let freeResolvesData; if (freeResolves) { let query; let diff, oldid; const specialDiff = /Special:Diff\/(\d+)(?:\/(\S+))?/i.exec(freeResolves); if (specialDiff) { if (specialDiff[2]) { oldid = specialDiff[1]; diff = specialDiff[2]; } else { diff = specialDiff[1]; } } else { diff = mw.util.getParamValue('diff', freeResolves); oldid = mw.util.getParamValue('oldid', freeResolves); } const title = mw.util.getParamValue('title', freeResolves); const diffNum = /^\d+$/.test(diff); // used repeatedly // rvdiffto in prop=revisions is deprecated, but action=compare doesn't return // timestamps ([[phab:T247686]]) so we can't rely on it unless necessary. // Likewise, we can't rely on a meaningful comment for diff=cur. // Additionally, links like Special:Diff/123/next, Special:Diff/123/456, or ?diff=next&oldid=123 // would each require making use of rvdir=newer in the revisions API. // That requires a title parameter, so we have to use compare instead of revisions. if (oldid && (diff === 'cur' || (!title && (diff === 'next' || diffNum)))) { query = { action: 'compare', fromrev: oldid, prop: 'ids|title', format: 'json' }; if (diffNum) { query.torev = diff; } else { query.torelative = diff; } } else { query = { action: 'query', prop: 'revisions', rvprop: 'ids|timestamp|comment', format: 'json', indexpageids: true }; if (diff && oldid) { if (diff === 'prev') { query.revids = oldid; } else { query.titles = title; query.rvdir = 'newer'; query.rvstartid = oldid; if (diff === 'next' && title) { query.rvlimit = 2; } else if (diffNum) { // Diffs may or may not be consecutive, no limit query.rvendid = diff; } } } else { // diff=next|prev|cur with no oldid // Implies title= exists otherwise it's not a valid diff link (well, it is, but to the Main Page) if (diff && /^\D+$/.test(diff)) { query.titles = title; } else { query.revids = diff || oldid; } } } let queryResponse; try { queryResponse = await new mw.Api().get(query); } catch (err) { const error = new Error('Call to MediaWiki API failed.'); error.cause = err; throw error; } if (queryResponse.compare && queryResponse.compare.fromtitle === queryResponse.compare.totitle) { freeResolvesData = queryResponse; } else if (queryResponse.query) { const pageIds = queryResponse.query.pageids; if (!Array.isArray(pageIds) || pageIds.length !== 1) { const error = new Error('Error parsing diff.'); error.cause = queryResponse; throw error; } freeResolvesData = queryResponse.query.pages[pageIds[0]]; } else { const error = new Error('Could not find any diff associated with the URL provided.'); error.cause = queryResponse; throw error; } } else { freeResolvesData = undefined; } if (!resolves.length && !freeResolvesData) { confirmations.push('You have not selected any edits where you tried to resolve the issue. Do you wish to make the report anyway?'); } const data = { uid: input.uid, page: input.page, comment: input.comment, diffs: diffs, warnings: warnings, resolves: resolves, free_resolves: freeResolvesData, confirmations: confirmations }; let minid; for (let i = 0; i < data.diffs.length; ++i) { if (data.diffs[i].parentid && (!minid || data.diffs[i].parentid < minid)) { minid = data.diffs[i].parentid; } } let queryResponse; try { queryResponse = await new mw.Api().get({ action: 'query', prop: 'revisions', format: 'json', rvprop: 'sha1|ids|timestamp|comment', rvlimit: 100, // intentionally limited rvstartid: minid, rvexcludeuser: data.uid, indexpageids: true, titles: data.page }); } catch (err) { const error = new Error('Call to MediaWiki API failed.'); error.cause = err; throw error; } // In case an edit summary was revdel'd const hasHiddenComment = function(rev) { if (!rev.comment && typeof rev.commenthidden === 'string') { return '(comment hidden)'; } // swap curly braces for HTML entities to avoid templates being rendered if they were included in an edit summary return '"' + rev.comment.replace(/\{/g, '&#123;').replace(/\}/g, '&#125;') + '"'; }; // TODO: The logic for filling the |orig= parameter in the report template seems to be broken. The API call does not seem to be parsed properly. It appears as if the data parameter of the callback is being used as if it were an array, but it isn't one - it's actually the entire API response object. I think adding queryResponse = queryResponse.query.pages[queryResponse.query.pageids[0]].revisions; after `let orig;` would convert it to the array it thinks it's dealing with, but I really have no idea what the actual goal is, so I can't figure out whether or not that's correct. -- Tollens let orig; if (queryResponse.length) { const sha1 = queryResponse[0].sha1; for (let i = 1; i < queryResponse.length; ++i) { if (queryResponse[i].sha1 === sha1) { orig = queryResponse[i]; break; } } if (!orig) { orig = queryResponse[0]; } } let origtext = ''; if (orig) { origtext = '{{diff2|' + orig.revid + '|' + orig.timestamp + '}} ' + hasHiddenComment(orig); } const groupedDiffs = {}; let parentid, lastid; for (let j = 0; j < data.diffs.length; ++j) { const cur = data.diffs[j]; if ((cur.revid && cur.revid !== parentid) || lastid === null) { lastid = cur.revid; groupedDiffs[lastid] = []; } parentid = cur.parentid; groupedDiffs[lastid].push(cur); } const difftext = $.map(groupedDiffs, (sub) => { let ret = ''; if (sub.length >= 2) { const last = sub[0]; const first = sub.slice(-1)[0]; const label = 'Consecutive edits made from ' + new Morebits.Date(first.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) to ' + new Morebits.Date(last.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)'; ret = '# {{diff|oldid=' + first.parentid + '|diff=' + last.revid + '|label=' + label + '}}\n'; } ret += sub.reverse().map((v) => (sub.length >= 2 ? '#' : '') + '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); return ret; }).reverse().join('\n'); const warningtext = data.warnings.reverse().map((v) => '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); let resolvetext = data.resolves.reverse().map((v) => '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); if (data.free_resolves) { const page = data.free_resolves; if (page.compare) { resolvetext += '\n# {{diff|oldid=' + page.compare.fromrevid + '|diff=' + page.compare.torevid + '|label=Consecutive edits on ' + page.compare.totitle + '}}'; } else if (page.revisions) { const revCount = page.revisions.length; let rev; if (revCount < 3) { // diff=prev or next rev = revCount === 1 ? page.revisions[0] : page.revisions[1]; resolvetext += '\n# {{diff2|' + rev.revid + '|' + new Morebits.Date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title + '}} ' + hasHiddenComment(rev); } else { // diff and oldid are nonconsecutive rev = page.revisions[0]; const revLatest = page.revisions[revCount - 1]; const label = 'Consecutive edits made from ' + new Morebits.Date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) to ' + new Morebits.Date(revLatest.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title; resolvetext += '\n# {{diff|oldid=' + rev.revid + '|diff=' + revLatest.revid + '|label=' + label + '}}\n'; } } } let comment = data.comment.replace(/~*$/g, '').trim(); if (comment) { comment += ' ~~~~'; } const reportWikitext = '\n\n{{subst:AN3 report|diffs=' + difftext + '|warnings=' + warningtext + '|resolves=' + resolvetext + '|pagename=' + data.page + '|orig=' + origtext + '|comment=' + comment + '|uid=' + data.uid + '}}'; return { uid: data.uid, reportWikitext: reportWikitext, confirmations: data.confirmations }; }; Twinkle.addInitCallback(Twinkle.arv, 'arv'); }()); // </nowiki> 2mjfv7l608t6q609qlhxwtyqkcpnc7w 365983 365982 2026-08-20T12:15:17Z ChiK 1136 ೧ revisions imported from [[:en:MediaWiki:Gadget-twinklearv.js]] 365982 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinklearv.js: ARV module **************************************** * Mode of invocation: Tab ("ARV") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.arv = function twinklearv() { const username = mw.config.get('wgRelevantUserName'); if (!username || username === mw.config.get('wgUserName')) { return; } const isIP = mw.util.isIPAddress(username, true); // Ignore ranges wider than the CIDR limit if (Morebits.ip.isRange(username) && !Morebits.ip.validCIDR(username)) { return; } const userType = isIP ? 'IP' + (Morebits.ip.isRange(username) ? ' range' : '') : 'user'; Twinkle.addPortletLink(() => { Twinkle.arv.callback(username, isIP); }, 'ARV', 'tw-arv', 'Report ' + userType + ' to administrators'); }; Twinkle.arv.callback = function(uid, isIP) { const Window = new Morebits.SimpleWindow(600, 500); Window.setTitle('Advance Reporting and Vetting'); // Backronym Window.setScriptName('Twinkle'); Window.addFooterLink('AIV guide', 'WP:GAIV'); Window.addFooterLink('UAA guide', 'WP:UAAI'); Window.addFooterLink('SPI guide', 'Wikipedia:Sockpuppet investigations/SPI/Guide to filing cases'); Window.addFooterLink('ARV prefs', 'WP:TW/PREF#arv'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#arv'); Window.addFooterLink('Give feedback', 'WT:TW'); const form = new Morebits.QuickForm(Twinkle.arv.callback.evaluate); form.append({ type: 'select', name: 'category', label: 'Select report type:', event: Twinkle.arv.callback.changeCategory, list: [ { label: 'Vandalism (WP:AIV)', value: 'aiv' }, { label: 'Username (WP:UAA)', value: 'username', disabled: isIP }, { label: 'Sockpuppeteer (WP:SPI)', value: 'sock' }, { label: 'Sockpuppet (WP:SPI)', value: 'puppet' }, { label: 'Edit warring (WP:AN3)', value: 'an3', disabled: Morebits.ip.isRange(uid) // rvuser template doesn't support ranges } ] }); form.append({ type: 'div', label: '', style: 'color: red', id: 'twinkle-arv-blockwarning' }); // Temporary account notice if (mw.config.get('wgRelevantUserName') && mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')) && !mw.config.get('wgCheckUserTemporaryAccountIPRevealAllowed')) { const temporaryAccountNotice = form.append({ type: 'field', label: 'Temporary account notice', name: 'ta_notice', style: 'color: var(--morebits-color-warning)' }); temporaryAccountNotice.append({ type: 'div', label: 'You are reporting a [[Wikipedia:Temporary accounts|temporary account]]. These accounts, with usernames like ~2025-12345-67, have replaced IP addresses. Please be careful when reporting for sockpuppetry and when blocking, because other users and temporary accounts on the same IP may end up as collateral damage.' }); } form.append({ type: 'field', label: 'Work area', name: 'work_area' }); form.append({ type: 'submit' }); form.append({ type: 'hidden', name: 'uid', value: uid }); const result = form.render(); Window.setContent(result); Window.display(); // Check if the user is blocked, update notice const query = { action: 'query', list: 'blocks', bkprop: 'range|flags', format: 'json' }; if (isIP) { query.bkip = uid; } else { query.bkusers = uid; } new Morebits.wiki.Api("Checking the user's block status", query, ((apiobj) => { const blocklist = apiobj.getResponse().query.blocks; if (blocklist.length) { // If an IP is blocked *and* rangeblocked, only use whichever is more recent const block = blocklist[0]; let message = (isIP ? 'This IP ' + (Morebits.ip.isRange(uid) ? 'range' : 'address') : 'This account') + ' is ' + (block.partial ? 'partially' : 'already') + ' blocked'; // Start and end differ, range blocked message += block.rangestart !== block.rangeend ? ' as part of a rangeblock.' : '.'; if (block.partial) { $('#twinkle-arv-blockwarning').css('color', 'black'); // Less severe } $('#twinkle-arv-blockwarning').text(message); } })).post(); // We must init the const evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.category.dispatchEvent(evt); }; Twinkle.arv.callback.changeCategory = function(e) { const value = e.target.value; const root = e.target.form; const old_area = Morebits.QuickForm.getElements(root, 'work_area')[0]; let work_area = null; switch (value) { case 'aiv': /* falls through */ default: work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report user for vandalism', name: 'work_area' }); work_area.append({ type: 'input', name: 'page', label: 'Primary linked page:', tooltip: 'Leave blank to not link to the page in the report', value: Twinkle.getPrefill('vanarticle') || '', event: function(e) { const value = e.target.value; const root = e.target.form; if (value === '') { root.badid.disabled = root.goodid.disabled = true; } else { root.badid.disabled = false; root.goodid.disabled = root.badid.value === ''; } } }); work_area.append({ type: 'input', name: 'badid', label: 'Revision ID for target page when vandalised:', tooltip: 'Leave blank for no diff link', value: Twinkle.getPrefill('vanarticlerevid') || '', disabled: !Twinkle.getPrefill('vanarticle'), event: function(e) { const value = e.target.value; const root = e.target.form; root.goodid.disabled = value === ''; } }); work_area.append({ type: 'input', name: 'goodid', label: 'Last good revision ID before vandalism of target page:', tooltip: 'Leave blank for diff link to previous revision', value: Twinkle.getPrefill('vanarticlegoodrevid') || '', disabled: !Twinkle.getPrefill('vanarticle') || Twinkle.getPrefill('vanarticlerevid') }); work_area.append({ type: 'checkbox', name: 'arvtype', list: [ { label: 'Vandalism after final (level 4 or 4im) warning given', value: 'final' }, { label: 'Vandalism after recent (within 1 day) release of block', value: 'postblock' }, { label: 'Evidently a vandalism-only account', value: 'vandalonly', disabled: mw.util.isIPAddress(root.uid.value, true) }, { label: 'Account is a promotion-only account', value: 'promoonly', disabled: mw.util.isIPAddress(root.uid.value, true) }, { label: 'Account is evidently a spambot or a compromised account', value: 'spambot' } ] }); work_area.append({ type: 'textarea', name: 'reason', label: 'Comment:' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'username': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report username violation', name: 'work_area' }); work_area.append({ type: 'header', label: 'Type(s) of inappropriate username', tooltip: 'Wikipedia does not allow usernames that are misleading, promotional, offensive or disruptive. Domain names and email addresses are likewise prohibited. These criteria apply to both usernames and signatures. Usernames that are inappropriate in another language, or that represent an inappropriate name with misspellings and substitutions, or do so indirectly or by implication, are still considered inappropriate.' }); work_area.append({ type: 'checkbox', name: 'arvtype', list: [ { label: 'Misleading username', value: 'misleading', tooltip: 'Misleading usernames imply relevant, misleading things about the contributor. For example, misleading points of fact, an impression of undue authority, or usernames giving the impression of a bot account.' }, { label: 'Promotional username', value: 'promotional', tooltip: 'Promotional usernames are advertisements for a company, website or group. Please do not report these names to UAA unless the user has also made promotional edits related to the name.' }, { label: 'Offensive username', value: 'offensive', tooltip: 'Offensive usernames make harmonious editing difficult or impossible.' }, { label: 'Disruptive username', value: 'disruptive', tooltip: 'Disruptive usernames include outright trolling or personal attacks, or otherwise show a clear intent to disrupt Wikipedia.' } ] }); work_area.append({ type: 'textarea', name: 'reason', label: 'Comment:' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'puppet': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report suspected sockpuppet', name: 'work_area' }); work_area.append( { type: 'input', name: 'sockmaster', label: 'Sockpuppeteer', tooltip: 'The username of the sockpuppeteer (sockmaster) without the "User:" prefix' } ); work_area.append({ type: 'textarea', label: 'Evidence:', name: 'evidence', tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'sock': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report suspected sockpuppeteer', name: 'work_area' }); work_area.append( { type: 'dyninput', name: 'sockpuppets', label: 'Sockpuppets', sublabel: 'Sock:', tooltip: 'The username of the sockpuppet without the "User:" prefix', min: 2 }); work_area.append({ type: 'textarea', label: 'Evidence:', name: 'evidence', tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'an3': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Report edit warring', name: 'work_area' }); work_area.append({ type: 'input', name: 'page', label: 'Page', tooltip: 'The page being reported' }); work_area.append({ type: 'button', name: 'load', label: 'Load', event: function(e) { const root = e.target.form; const date = new Morebits.Date().subtract(48, 'hours'); // all since 48 hours // Run for each AN3 field const getAN3Entries = function(field, rvuser, titles) { const $field = $(root).find('[name=' + field + ']'); $field.find('.entry').remove(); new mw.Api().get({ action: 'query', prop: 'revisions', format: 'json', rvprop: 'sha1|ids|timestamp|parsedcomment|comment', rvlimit: 500, // intentionally limited rvend: date.toISOString(), rvuser: rvuser, indexpageids: true, titles: titles }).done((data) => { const pageid = data.query.pageids[0]; const page = data.query.pages[pageid]; if (!page.revisions) { $('<span>') .addClass('entry') .html('None found') .appendTo($field); } else { for (let i = 0; i < page.revisions.length; ++i) { const rev = page.revisions[i]; const $entry = $('<div>') .addClass('entry'); const $input = $('<input>') .attr({ type: 'checkbox', name: 's_' + field, value: JSON.stringify(rev) }); $input.appendTo($entry); let comment = '<span>'; // revdel/os if (typeof rev.commenthidden === 'string') { comment += '(comment hidden)'; } else { comment += '"' + rev.parsedcomment + '"'; } comment += ' at <a href="' + mw.config.get('wgScript') + '?diff=' + rev.revid + '">' + new Morebits.Date(rev.timestamp).calendar() + '</a></span>'; $entry.append(comment).appendTo($field); } } // add free form input for resolves if (field === 'resolves') { const $free_entry = $('<div>') .addClass('entry'); const $free_input = $('<input>') .attr({ type: 'text', name: 's_resolves_free' }); const $free_label = $('<label>') .attr('for', 's_resolves_free') .html('URL link of diff with additional discussions: '); $free_entry.append($free_label).append($free_input).appendTo($field); } }).fail(() => { $('<span>') .addClass('entry') .html('API failure, reload page and try again') .appendTo($field); }); }; // warnings const uid = root.uid.value; getAN3Entries('warnings', mw.config.get('wgUserName'), 'User talk:' + uid); // diffs and resolves require a valid page const page = root.page.value; if (page) { // diffs getAN3Entries('diffs', uid, page); // resolutions const t = new mw.Title(page); const talk_page = t.getTalkPage().getPrefixedText(); getAN3Entries('resolves', mw.config.get('wgUserName'), talk_page); } else { $(root).find('[name=diffs]').find('.entry').remove(); $(root).find('[name=resolves]').find('.entry').remove(); } } }); work_area.append({ type: 'field', name: 'diffs', label: 'User\'s reverts (within last 48 hours)', tooltip: 'Select the edits you believe are reverts' }); work_area.append({ type: 'field', name: 'warnings', label: 'Warnings given to subject', tooltip: 'You must have warned the subject before reporting' }); work_area.append({ type: 'field', name: 'resolves', label: 'Resolution initiatives', tooltip: 'You should have tried to resolve the issue on the talk page first' }); work_area.append({ type: 'textarea', label: 'Comment:', name: 'comment' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; } }; Twinkle.arv.callback.evaluate = function(e) { const form = e.target; let reason = ''; const input = Morebits.QuickForm.getInputData(form); switch (input.category) { // Report user for vandalism case 'aiv': /* falls through */ default: reason = Twinkle.arv.callback.getAivReasonWikitext(input); if (reason === null) { alert('You must specify some reason'); return; } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.actionCompleted.redirect = 'Wikipedia:Administrator intervention against vandalism'; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; var aivPage = new Morebits.wiki.Page('Wikipedia:Administrator intervention against vandalism', 'Processing AIV request'); aivPage.setPageSection(1); aivPage.setFollowRedirect(true); aivPage.load(() => { const text = aivPage.getPageText(); const $aivLink = '<a target="_blank" href="/wiki/WP:AIV">WP:AIV</a>'; // check if user has already been reported if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(input.uid) + '\\s*\\}\\}').test(text)) { aivPage.getStatusElement().error('Report already present, will not add a new one'); Morebits.Status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at ' + $aivLink + ':'); return; } // then check for any bot reports const tb2Page = new Morebits.wiki.Page('Wikipedia:Administrator intervention against vandalism/TB2', 'Checking bot reports'); tb2Page.load(() => { const tb2Text = tb2Page.getPageText(); const tb2statelem = tb2Page.getStatusElement(); if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(input.uid) + '\\s*\\}\\}').test(tb2Text)) { if (confirm('The user ' + input.uid + ' has already been reported by a bot. Do you wish to make the report anyway?')) { tb2statelem.info('Proceeded despite bot report'); } else { tb2statelem.error('Report from a bot is already present, stopping'); Morebits.Status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them at ' + $aivLink + ':'); return; } } else { tb2statelem.info('No conflicting bot reports'); } aivPage.getStatusElement().status('Adding new report...'); aivPage.setEditSummary('Reporting [[Special:Contributions/' + input.uid + '|' + input.uid + ']].'); aivPage.setChangeTags(Twinkle.changeTags); aivPage.setAppendText(Twinkle.arv.callback.buildAivReport(input)); aivPage.setDiscussionToolsAutoSubscribe(false); aivPage.append(); }); }); break; // Report inappropriate username case 'username': var censorUsername = input.arvtype.includes('offensive'); // check if the username is marked offensive reason = Twinkle.arv.callback.getUsernameReportWikitext(input); Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.actionCompleted.redirect = 'Wikipedia:Usernames for administrator attention'; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; var uaaPage = new Morebits.wiki.Page('Wikipedia:Usernames for administrator attention', 'Processing UAA request'); uaaPage.setFollowRedirect(true); uaaPage.load(() => { const text = uaaPage.getPageText(); // check if user has already been reported if (new RegExp('\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?' + Morebits.string.escapeRegExp(input.uid) + '\\s*(\\||\\})').test(text)) { uaaPage.getStatusElement().error('User is already listed.'); const $uaaLink = '<a target="_blank" href="/wiki/WP:UAA">WP:UAA</a>'; Morebits.Status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at ' + $uaaLink + ':'); return; } uaaPage.getStatusElement().status('Adding new report...'); uaaPage.setEditSummary('Reporting ' + (censorUsername ? 'an offensive username.' : '[[Special:Contributions/' + input.uid + '|' + input.uid + ']].')); uaaPage.setChangeTags(Twinkle.changeTags); // Blank newline per [[Special:Permalink/996949310#Spacing]]; see also [[WP:LISTGAP]] and [[WP:INDENTGAP]] uaaPage.setPageText(text + '\n' + reason + '\n*'); uaaPage.setDiscussionToolsAutoSubscribe(false); uaaPage.save(); }); break; // WP:SPI case 'sock': /* falls through */ case 'puppet': var reportData = Twinkle.arv.callback.getSpiReportData(input); if (reportData.error) { alert(reportData.error); return; } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" var reportpage = 'Wikipedia:Sockpuppet investigations/' + reportData.sockmaster; Morebits.wiki.actionCompleted.redirect = reportpage; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; var spiPage = new Morebits.wiki.Page(reportpage, 'Retrieving discussion page'); spiPage.setFollowRedirect(true); spiPage.setEditSummary('Adding new report for [[Special:Contributions/' + reportData.sockmaster + '|' + reportData.sockmaster + ']].'); spiPage.setChangeTags(Twinkle.changeTags); spiPage.setAppendText(reportData.wikitext); spiPage.setWatchlist(Twinkle.getPref('spiWatchReport')); spiPage.setDiscussionToolsAutoSubscribe(false); spiPage.append(); Morebits.wiki.removeCheckpoint(); // all page updates have been started break; case 'an3': // prepare the AN3 report, then post and notify Twinkle.arv.callback.getAn3ReportData(input).then((data) => { // If there are any reasons why the user might want to cancel the report, check with them about each reason and cancel if they choose to for (const confirmation of data.confirmations) { if (!confirm(confirmation)) { return; } } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" const reportpage = 'Wikipedia:Administrators\' noticeboard/Edit warring'; Morebits.wiki.actionCompleted.redirect = reportpage; Morebits.wiki.actionCompleted.notice = 'Reporting complete'; const an3Page = new Morebits.wiki.Page(reportpage, 'Retrieving discussion page'); an3Page.setFollowRedirect(true); an3Page.setEditSummary('Adding new report for [[Special:Contributions/' + data.uid + '|' + data.uid + ']].'); an3Page.setChangeTags(Twinkle.changeTags); an3Page.setAppendText(data.reportWikitext); an3Page.setDiscussionToolsAutoSubscribe(false); an3Page.append(); // notify user const notifyText = '\n\n{{subst:an3-notice|1=' + mw.util.wikiUrlencode(data.uid) + '|auto=1}} ~~~~'; const talkPage = new Morebits.wiki.Page('User talk:' + data.uid, 'Notifying edit warrior'); talkPage.setFollowRedirect(true); talkPage.setEditSummary('Notifying about edit warring noticeboard discussion.'); talkPage.setChangeTags(Twinkle.changeTags); talkPage.setAppendText(notifyText); talkPage.append(); Morebits.wiki.removeCheckpoint(); // all page updates have been started }).catch((error) => { console.error('Error occurred while preparing AN3 report.', error); // eslint-disable-line no-console alert('Error occurred while preparing AN3 report: ' + error.message); }); break; } }; Twinkle.arv.callback.getAivReasonWikitext = function(input) { let text = ''; let type = input.arvtype; if (!type.length && input.reason === '') { return null; } type = type.map((v) => { switch (v) { case 'final': return 'vandalism after final warning'; case 'postblock': return 'vandalism after recent release of block'; case 'vandalonly': return 'actions evidently indicate a vandalism-only account'; case 'promoonly': return 'account is being used only for promotional purposes'; case 'spambot': return 'account is evidently a spambot or a compromised account'; default: return 'unknown reason'; } }).join('; '); if (input.page !== '') { // Allow links to redirects, files, and categories text = 'On {{No redirect|' + input.page + '}}'; if (input.badid !== '') { text += ' ({{diff|' + input.page + '|' + input.badid + '|' + input.goodid + '|diff}})'; } text += ':'; } if (type) { text += ' ' + type; } if (input.reason !== '') { const textEndsInPunctuationOrBlank = /([.?!;:]|^)$/.test(text); text += textEndsInPunctuationOrBlank ? '' : '.'; const textIsBlank = text === ''; text += textIsBlank ? '' : ' '; text += input.reason; } text = text.trim(); const textEndsInPunctuation = /[.?!;]$/.test(text); if (!textEndsInPunctuation) { text += '.'; } text += ' ~~~~'; text = text.replace(/\r?\n/g, '\n*:'); // indent newlines return text; }; Twinkle.arv.callback.buildAivReport = function(input) { return '\n*{{vandal|' + (/=/.test(input.uid) ? '1=' : '') + input.uid + '}} &ndash; ' + Twinkle.arv.callback.getAivReasonWikitext(input); }; Twinkle.arv.callback.getUsernameReportWikitext = function(input) { // generate human-readable string, e.g. "misleading and promotional username" if (input.arvtype.length <= 2) { input.arvtype = input.arvtype.join(' and '); } else { input.arvtype = [input.arvtype.slice(0, -1).join(', '), input.arvtype.slice(-1)].join(' and '); } // a or an? let adjective = 'a'; if (/[aeiouwyh]/.test(input.arvtype[0] || '')) { // non 100% correct, but whatever, including 'h' for Cockney adjective = 'an'; } let text = '*{{user-uaa|1=' + input.uid + '}} &ndash; '; if (input.arvtype.length) { text += 'Violation of the username policy as ' + adjective + ' ' + input.arvtype + ' username. '; } if (input.reason !== '') { text += Morebits.string.toUpperCaseFirstChar(input.reason); const endsInPeriod = /\.$/.test(input.reason); if (!endsInPeriod) { text += '.'; } text += ' '; } text += '~~~~'; text = text.replace(/\r?\n/g, '\n*:'); // indent newlines return text; }; Twinkle.arv.callback.getSpiReportData = function(input) { const isPuppetReport = input.category === 'puppet'; if (!isPuppetReport) { input.sockpuppets = input.sockpuppets.filter((sock) => sock !== ''); // ignore empty sockpuppet inputs } if (isPuppetReport && !input.sockmaster) { return { error: 'You have not entered a sockmaster account for this puppet. Consider reporting this account as a sockpuppeteer instead.' }; } else if (!isPuppetReport && input.sockpuppets.length === 0) { return { error: 'You have not entered any sockpuppet account(s) for this sockmaster. Consider reporting this account as a sockpuppet instead.' }; } input.sockmaster = input.sockmaster || input.uid; input.sockpuppets = isPuppetReport ? [input.uid] : Morebits.array.uniq(input.sockpuppets); let text = '\n{{subst:SPI report|' + input.sockpuppets.map((sock, index) => (index + 1) + '=' + sock).join('|') + '\n|evidence=' + Morebits.string.formatReasonText(input.evidence) + ' \n'; text += '}}'; return { sockmaster: input.sockmaster, wikitext: text }; }; Twinkle.arv.callback.getAn3ReportData = async function(input) { const confirmations = []; const diffs = input.s_diffs ? input.s_diffs.map(JSON.parse) : []; if (diffs.length < 3) { confirmations.push('You have selected fewer than three offending edits. Do you wish to make the report anyway?'); } const warnings = input.s_warnings ? input.s_warnings.map(JSON.parse) : []; if (!warnings.length) { confirmations.push('You have not selected any edits where you warned the offender. Do you wish to make the report anyway?'); } const resolves = input.s_resolves ? input.s_resolves.map(JSON.parse) : []; const freeResolves = input.s_resolves_free; let freeResolvesData; if (freeResolves) { let query; let diff, oldid; const specialDiff = /Special:Diff\/(\d+)(?:\/(\S+))?/i.exec(freeResolves); if (specialDiff) { if (specialDiff[2]) { oldid = specialDiff[1]; diff = specialDiff[2]; } else { diff = specialDiff[1]; } } else { diff = mw.util.getParamValue('diff', freeResolves); oldid = mw.util.getParamValue('oldid', freeResolves); } const title = mw.util.getParamValue('title', freeResolves); const diffNum = /^\d+$/.test(diff); // used repeatedly // rvdiffto in prop=revisions is deprecated, but action=compare doesn't return // timestamps ([[phab:T247686]]) so we can't rely on it unless necessary. // Likewise, we can't rely on a meaningful comment for diff=cur. // Additionally, links like Special:Diff/123/next, Special:Diff/123/456, or ?diff=next&oldid=123 // would each require making use of rvdir=newer in the revisions API. // That requires a title parameter, so we have to use compare instead of revisions. if (oldid && (diff === 'cur' || (!title && (diff === 'next' || diffNum)))) { query = { action: 'compare', fromrev: oldid, prop: 'ids|title', format: 'json' }; if (diffNum) { query.torev = diff; } else { query.torelative = diff; } } else { query = { action: 'query', prop: 'revisions', rvprop: 'ids|timestamp|comment', format: 'json', indexpageids: true }; if (diff && oldid) { if (diff === 'prev') { query.revids = oldid; } else { query.titles = title; query.rvdir = 'newer'; query.rvstartid = oldid; if (diff === 'next' && title) { query.rvlimit = 2; } else if (diffNum) { // Diffs may or may not be consecutive, no limit query.rvendid = diff; } } } else { // diff=next|prev|cur with no oldid // Implies title= exists otherwise it's not a valid diff link (well, it is, but to the Main Page) if (diff && /^\D+$/.test(diff)) { query.titles = title; } else { query.revids = diff || oldid; } } } let queryResponse; try { queryResponse = await new mw.Api().get(query); } catch (err) { const error = new Error('Call to MediaWiki API failed.'); error.cause = err; throw error; } if (queryResponse.compare && queryResponse.compare.fromtitle === queryResponse.compare.totitle) { freeResolvesData = queryResponse; } else if (queryResponse.query) { const pageIds = queryResponse.query.pageids; if (!Array.isArray(pageIds) || pageIds.length !== 1) { const error = new Error('Error parsing diff.'); error.cause = queryResponse; throw error; } freeResolvesData = queryResponse.query.pages[pageIds[0]]; } else { const error = new Error('Could not find any diff associated with the URL provided.'); error.cause = queryResponse; throw error; } } else { freeResolvesData = undefined; } if (!resolves.length && !freeResolvesData) { confirmations.push('You have not selected any edits where you tried to resolve the issue. Do you wish to make the report anyway?'); } const data = { uid: input.uid, page: input.page, comment: input.comment, diffs: diffs, warnings: warnings, resolves: resolves, free_resolves: freeResolvesData, confirmations: confirmations }; let minid; for (let i = 0; i < data.diffs.length; ++i) { if (data.diffs[i].parentid && (!minid || data.diffs[i].parentid < minid)) { minid = data.diffs[i].parentid; } } let queryResponse; try { queryResponse = await new mw.Api().get({ action: 'query', prop: 'revisions', format: 'json', rvprop: 'sha1|ids|timestamp|comment', rvlimit: 100, // intentionally limited rvstartid: minid, rvexcludeuser: data.uid, indexpageids: true, titles: data.page }); } catch (err) { const error = new Error('Call to MediaWiki API failed.'); error.cause = err; throw error; } // In case an edit summary was revdel'd const hasHiddenComment = function(rev) { if (!rev.comment && typeof rev.commenthidden === 'string') { return '(comment hidden)'; } // swap curly braces for HTML entities to avoid templates being rendered if they were included in an edit summary return '"' + rev.comment.replace(/\{/g, '&#123;').replace(/\}/g, '&#125;') + '"'; }; // TODO: The logic for filling the |orig= parameter in the report template seems to be broken. The API call does not seem to be parsed properly. It appears as if the data parameter of the callback is being used as if it were an array, but it isn't one - it's actually the entire API response object. I think adding queryResponse = queryResponse.query.pages[queryResponse.query.pageids[0]].revisions; after `let orig;` would convert it to the array it thinks it's dealing with, but I really have no idea what the actual goal is, so I can't figure out whether or not that's correct. -- Tollens let orig; if (queryResponse.length) { const sha1 = queryResponse[0].sha1; for (let i = 1; i < queryResponse.length; ++i) { if (queryResponse[i].sha1 === sha1) { orig = queryResponse[i]; break; } } if (!orig) { orig = queryResponse[0]; } } let origtext = ''; if (orig) { origtext = '{{diff2|' + orig.revid + '|' + orig.timestamp + '}} ' + hasHiddenComment(orig); } const groupedDiffs = {}; let parentid, lastid; for (let j = 0; j < data.diffs.length; ++j) { const cur = data.diffs[j]; if ((cur.revid && cur.revid !== parentid) || lastid === null) { lastid = cur.revid; groupedDiffs[lastid] = []; } parentid = cur.parentid; groupedDiffs[lastid].push(cur); } const difftext = $.map(groupedDiffs, (sub) => { let ret = ''; if (sub.length >= 2) { const last = sub[0]; const first = sub.slice(-1)[0]; const label = 'Consecutive edits made from ' + new Morebits.Date(first.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) to ' + new Morebits.Date(last.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)'; ret = '# {{diff|oldid=' + first.parentid + '|diff=' + last.revid + '|label=' + label + '}}\n'; } ret += sub.reverse().map((v) => (sub.length >= 2 ? '#' : '') + '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); return ret; }).reverse().join('\n'); const warningtext = data.warnings.reverse().map((v) => '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); let resolvetext = data.resolves.reverse().map((v) => '# {{diff2|' + v.revid + '|' + new Morebits.Date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v)).join('\n'); if (data.free_resolves) { const page = data.free_resolves; if (page.compare) { resolvetext += '\n# {{diff|oldid=' + page.compare.fromrevid + '|diff=' + page.compare.torevid + '|label=Consecutive edits on ' + page.compare.totitle + '}}'; } else if (page.revisions) { const revCount = page.revisions.length; let rev; if (revCount < 3) { // diff=prev or next rev = revCount === 1 ? page.revisions[0] : page.revisions[1]; resolvetext += '\n# {{diff2|' + rev.revid + '|' + new Morebits.Date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title + '}} ' + hasHiddenComment(rev); } else { // diff and oldid are nonconsecutive rev = page.revisions[0]; const revLatest = page.revisions[revCount - 1]; const label = 'Consecutive edits made from ' + new Morebits.Date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) to ' + new Morebits.Date(revLatest.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title; resolvetext += '\n# {{diff|oldid=' + rev.revid + '|diff=' + revLatest.revid + '|label=' + label + '}}\n'; } } } let comment = data.comment.replace(/~*$/g, '').trim(); if (comment) { comment += ' ~~~~'; } const reportWikitext = '\n\n{{subst:AN3 report|diffs=' + difftext + '|warnings=' + warningtext + '|resolves=' + resolvetext + '|pagename=' + data.page + '|orig=' + origtext + '|comment=' + comment + '|uid=' + data.uid + '}}'; return { uid: data.uid, reportWikitext: reportWikitext, confirmations: data.confirmations }; }; Twinkle.addInitCallback(Twinkle.arv, 'arv'); }()); // </nowiki> 2mjfv7l608t6q609qlhxwtyqkcpnc7w ಮಾದ್ಯಮೊವಿಕಿ:Gadget-twinkleblock.js 8 25386 365988 2015-06-08T14:58:07Z en>MusikAnimal 0 v2.0 at 465eb53: Block module for administrators 365988 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: [{ label: '', value: '', type: 'option' }].concat(Twinkle.block.callback.filtered_block_groups()) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: Twinkle.block.field_template_options.article || ( Morebits.queryString.exists( 'vanarticle' ) ? Morebits.queryString.get( 'vanarticle' ) : '' ), tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: Twinkle.block.field_template_options.template_expiry || Twinkle.block.field_block_options.expiry || $form.find('[name=template_expiry]').val() || '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, reason: '{{blocked proxy}}', sig: null }, 'blocked talk-revoked-notice' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'CheckUser block' : { reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-aoablock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been block from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.expiry = settings.expiry || '31 hours'; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; Twinkle.block.blockPresetsInfo[preset] = settings; }); }; Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, { label: 'Generic block (custom reason) – IP', value: 'uw-ablock' }, { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'blocked talk-revoked-notice' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-aoablock' }, { label: 'Possible comprimised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }] }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if( Twinkle.block.blockPresetsInfo[value].indefinite ) { if(Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!Twinkle.block.blockPresetsInfo[value].pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!Twinkle.block.blockPresetsInfo[value].reasonParam); e.target.form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var templatename = form.template.value, linkedarticle = form.article.value, expiry = form.template_expiry ? form.template_expiry.value : form.expiry.value, isIndefinite = !!(/indef|infinity|never|\*|max/).exec( expiry ); var templatetext = Twinkle.block.callback.getBlockNoticeWikitext(templatename, linkedarticle, expiry, form.block_reason.value, isIndefinite); form.previewer.beginRender(templatetext); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.warn_user(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.warn_user(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.warn_user = function twinkleblockCallbackWarnUser(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template] }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(templateName, article, blockTime, blockReason, isIndefinite) { var text = '{{subst:' + templateName, settings = Twinkle.block.blockPresetsInfo[templateName]; if (article && settings.pageParam) text += '|page=' + article; if (!/te?mp|^\s*$|min/.exec(blockTime)) { if (isIndefinite) { text += '|indef=yes'; } else { text += '|time=' + blockTime; } } if (blockReason) text += '|reason=' + blockReason; if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if( text.length > 0 ) { text += '\n\n'; } var isIndefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && isIndefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } text += Twinkle.block.callback.getBlockNoticeWikitext(params.template, params.article, params.expiry, params.block_reason, isIndefinite); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> te9088oi1xri5b89jk6vopq530pb74m 365989 365988 2015-06-08T16:55:40Z en>MusikAnimal 0 trying reordering to address load time issue for some users 365989 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: [{ label: '', value: '', type: 'option' }].concat(Twinkle.block.callback.filtered_block_groups()) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: Twinkle.block.field_template_options.article || ( Morebits.queryString.exists( 'vanarticle' ) ? Morebits.queryString.get( 'vanarticle' ) : '' ), tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: Twinkle.block.field_template_options.template_expiry || Twinkle.block.field_block_options.expiry || $form.find('[name=template_expiry]').val() || '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, reason: '{{blocked proxy}}', sig: null }, 'blocked talk-revoked-notice' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'CheckUser block' : { reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-aoablock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been block from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.expiry = settings.expiry || '31 hours'; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; Twinkle.block.blockPresetsInfo[preset] = settings; }); }; Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, { label: 'Generic block (custom reason) – IP', value: 'uw-ablock' }, { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'blocked talk-revoked-notice' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-aoablock' }, { label: 'Possible comprimised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }] }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if( Twinkle.block.blockPresetsInfo[value].indefinite ) { if(Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!Twinkle.block.blockPresetsInfo[value].pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!Twinkle.block.blockPresetsInfo[value].reasonParam); e.target.form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var templatename = form.template.value, linkedarticle = form.article.value, expiry = form.template_expiry ? form.template_expiry.value : form.expiry.value, isIndefinite = !!(/indef|infinity|never|\*|max/).exec( expiry ); var templatetext = Twinkle.block.callback.getBlockNoticeWikitext(templatename, linkedarticle, expiry, form.block_reason.value, isIndefinite); form.previewer.beginRender(templatetext); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.warn_user(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.warn_user(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.warn_user = function twinkleblockCallbackWarnUser(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template] }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(templateName, article, blockTime, blockReason, isIndefinite) { var text = '{{subst:' + templateName, settings = Twinkle.block.blockPresetsInfo[templateName]; if (article && settings.pageParam) text += '|page=' + article; if (!/te?mp|^\s*$|min/.exec(blockTime)) { if (isIndefinite) { text += '|indef=yes'; } else { text += '|time=' + blockTime; } } if (blockReason) text += '|reason=' + blockReason; if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if( text.length > 0 ) { text += '\n\n'; } var isIndefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && isIndefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } text += Twinkle.block.callback.getBlockNoticeWikitext(params.template, params.article, params.expiry, params.block_reason, isIndefinite); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> l0jf02dldz9pjzkwms9p02z9ubsg3x7 365990 365989 2015-06-08T16:59:23Z en>MusikAnimal 0 Undid revision 666059547 by [[Special:Contributions/MusikAnimal|MusikAnimal]] ([[User talk:MusikAnimal|talk]]) 365990 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: [{ label: '', value: '', type: 'option' }].concat(Twinkle.block.callback.filtered_block_groups()) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: Twinkle.block.field_template_options.article || ( Morebits.queryString.exists( 'vanarticle' ) ? Morebits.queryString.get( 'vanarticle' ) : '' ), tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: Twinkle.block.field_template_options.template_expiry || Twinkle.block.field_block_options.expiry || $form.find('[name=template_expiry]').val() || '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, reason: '{{blocked proxy}}', sig: null }, 'blocked talk-revoked-notice' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'CheckUser block' : { reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-aoablock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been block from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.expiry = settings.expiry || '31 hours'; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; Twinkle.block.blockPresetsInfo[preset] = settings; }); }; Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, { label: 'Generic block (custom reason) – IP', value: 'uw-ablock' }, { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'blocked talk-revoked-notice' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-aoablock' }, { label: 'Possible comprimised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }] }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if( Twinkle.block.blockPresetsInfo[value].indefinite ) { if(Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!Twinkle.block.blockPresetsInfo[value].pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!Twinkle.block.blockPresetsInfo[value].reasonParam); e.target.form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var templatename = form.template.value, linkedarticle = form.article.value, expiry = form.template_expiry ? form.template_expiry.value : form.expiry.value, isIndefinite = !!(/indef|infinity|never|\*|max/).exec( expiry ); var templatetext = Twinkle.block.callback.getBlockNoticeWikitext(templatename, linkedarticle, expiry, form.block_reason.value, isIndefinite); form.previewer.beginRender(templatetext); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.warn_user(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.warn_user(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.warn_user = function twinkleblockCallbackWarnUser(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template] }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(templateName, article, blockTime, blockReason, isIndefinite) { var text = '{{subst:' + templateName, settings = Twinkle.block.blockPresetsInfo[templateName]; if (article && settings.pageParam) text += '|page=' + article; if (!/te?mp|^\s*$|min/.exec(blockTime)) { if (isIndefinite) { text += '|indef=yes'; } else { text += '|time=' + blockTime; } } if (blockReason) text += '|reason=' + blockReason; if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if( text.length > 0 ) { text += '\n\n'; } var isIndefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && isIndefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } text += Twinkle.block.callback.getBlockNoticeWikitext(params.template, params.article, params.expiry, params.block_reason, isIndefinite); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> te9088oi1xri5b89jk6vopq530pb74m 365991 365990 2015-06-11T00:17:22Z en>MusikAnimal 0 v2.0 at ca38f90: updates in response feedback at [[WP:AN#New Twinkle block module!]] 365991 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: [{ label: '', value: '', type: 'option' }].concat(Twinkle.block.callback.filtered_block_groups()) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure right fields are hidden based on default template Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'blocked talk-revoked-notice' : { disabletalk: true, nonstandard: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-aoablock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been block from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.expiry = settings.expiry || '31 hours'; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; Twinkle.block.blockPresetsInfo[preset] = settings; }); }; Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, { label: 'Generic block (custom reason) – IP', value: 'uw-ablock' }, { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'blocked talk-revoked-notice' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-aoablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }] }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { template: form.template.value, article: form.article.value, expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, indefinite: !!(/indef|infinity|never|\*|max/).exec( form.template_expiry ? form.template_expiry.value : form.expiry.value ), blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), reason: form.block_reason.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> ep7edekdp7vo25zbr4rt0rksa5c048p 365992 365991 2015-06-17T15:13:42Z en>Amalthea 0 v2.0-830-gb28ba500a899: block: make generic block the default preset when module is loaded, moved disruptive editing to the list of common block reasons, added support for page parameter to edit warring template, and fixed a typo 365992 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'blocked talk-revoked-notice' : { disabletalk: true, nonstandard: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-aoablock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.expiry = settings.expiry || '31 hours'; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'blocked talk-revoked-notice' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-aoablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { template: form.template.value, article: form.article.value, expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, indefinite: !!(/indef|infinity|never|\*|max/).exec( form.template_expiry ? form.template_expiry.value : form.expiry.value ), blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), reason: form.block_reason.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> sfftupc27q5bkwqd8f9ecs13lusekz2 365993 365992 2015-06-28T16:29:25Z en>MusikAnimal 0 v2 at d5d8014: fixes for revoking talk page access, reblocking, disable autoblock if blocking a bot 365993 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { template: form.template.value, article: form.article.value, expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, indefinite: !!(/indef|infinity|never|\*|max/).exec( form.template_expiry ? form.template_expiry.value : form.expiry.value ), blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), reason: form.block_reason.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> egw0c3a2i3e8zapcg2hiji09azrxlm5 365994 365993 2015-06-28T16:46:43Z en>MusikAnimal 0 Undid revision 669061655 by [[Special:Contributions/MusikAnimal|MusikAnimal]] ([[User talk:MusikAnimal|talk]]) broke indef blocks 365994 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'blocked talk-revoked-notice' : { disabletalk: true, nonstandard: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-aoablock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.expiry = settings.expiry || '31 hours'; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'blocked talk-revoked-notice' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-aoablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { template: form.template.value, article: form.article.value, expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, indefinite: !!(/indef|infinity|never|\*|max/).exec( form.template_expiry ? form.template_expiry.value : form.expiry.value ), blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), reason: form.block_reason.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = !!(/indef|infinity|never|\*|max/).exec( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> sfftupc27q5bkwqd8f9ecs13lusekz2 365995 365994 2015-06-29T00:47:13Z en>MusikAnimal 0 v2.0 at 65b516c: fixes for revoking talk page access, reblocking, disable autoblock if blocking a bot, anon=yes only for soft block 365995 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sockpuppetry', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> kgx2etvw0d0v43vsfhwxtu55fkyz72l 365996 365995 2015-07-28T15:17:32Z en>MusikAnimal 0 v2.0 at 98d0947: add {{uw-sockblock}} for when blocking sockmasters and make {{uw-spoablock}} indefinite 365996 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry_preset = form.expiry_preset, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> 0dgtonilwy3dsp1q36zjsd1anqt6090 365997 365996 2015-11-01T23:58:52Z en>MusikAnimal 0 v2.0 at eba1580: Fixes from jshint 365997 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> rscfn3peicchbdfmxajwd4qkynpom4m 365998 365997 2016-08-30T21:15:40Z en>MusikAnimal 0 v2.0 at a662b83: adding new {{uw-ucblock}} (unsourced content) block rationale 365998 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> dpe3qp6t8uzklaffrecyq82nwsofnjs 365999 365998 2016-09-14T21:11:58Z en>MusikAnimal 0 v2.0 at 3614dab: add preset for {{spamblacklistblock}} 365999 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> m5zog8i23kevjqrhsds7clme8nho94u 366000 365999 2016-09-14T22:07:41Z en>MusikAnimal 0 v2.0 at f2615af: make {{spamblacklistblock}} for anons only 366000 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> gha69vcygkeuojsauwrgvu7muh8b96n 366001 366000 2016-12-11T21:59:14Z en>MusikAnimal 0 v2.0 at 2a1885d: add 'see also' block summary helpers, sensible default options for CheckUser blocks 366001 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso(e) { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> nzx99rkjmg34mlisdpeklsnii3d3vhd 366002 366001 2016-12-23T01:47:27Z en>MusikAnimal 0 v2.0 at 7ae20ba: add {{zombie proxy}} preset 366002 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso(e) { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> 9ahakn9219ewg2qk6kujsuy0jmo7t2u 366003 366002 2016-12-23T01:52:33Z en>MusikAnimal 0 v2.0 at 6b8c0f2: fix typo, {{zombie proxy}} not {{blocked proxy}} 366003 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso(e) { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> dwi5gm7bgpp69626jrhvxxer65gzc2v 366004 366003 2016-12-27T03:51:57Z en>MusikAnimal 0 v2.0 at 8ecf212: Stronger default settings for oversight blocks 366004 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read \"You have been blocked from editing temporarily for...\"' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso(e) { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function(data) { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> j5vvuer11ezcmv5qtauf3jkogsen0a8 366005 366004 2018-03-15T18:15:36Z en>MusikAnimal 0 v2.0 at edf3881: Suppress edit summary pings by prefixing links with a colon (T189067) 366005 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating inappropriate pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> cbpaxxlregzndpgswrmpa1p3qs68swm 366006 366005 2018-08-28T23:09:50Z en>MusikAnimal 0 v2.0 at 4df87e3: Make dropdown text for nonsense block explicit 366006 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> l1q98fv9emxbpkm7jyt1qsukwhuwrx6 366007 366006 2019-01-11T03:34:13Z en>MusikAnimal 0 v2.0 at 3d8ed63: per WP:HARDBLOCK proxies are hard-blocked on detection 366007 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-memorialblock': { forRegisteredOnly: true, expiry: 'infinity', reason: '{{uw-memorialblock}} <!-- Username indicates tribute to someone, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this account may be used as a memorial or tribute to someone' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Memorial username soft block', value: 'uw-memorialblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> terefrgys617rv9csuhqqls0992ow4q 366008 366007 2019-01-22T18:04:55Z en>Amorymeltzer 0 Repo at a999149: Remove uw-memorialblock 366008 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText); }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> ao99px8va4lri449e9h0l7cliy0gukx 366009 366008 2019-03-08T16:39:46Z en>Amorymeltzer 0 Repo at 79b422a: Make sure the preview renderer uses wikitext 366009 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked article', value: '', tooltip: 'An article can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no article to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'WP:TW'); // Force wikitext }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> fcqjf35jpoqpnercpbxj7bcfrih0wjc 366010 366009 2019-04-03T15:16:39Z en>Amorymeltzer 0 Repo at 26305a2: Use 'page' instead of 'article' in label/tooltip; Use wgRelevantUserName in previewer 366010 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc, use "infinity" for indefinite> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinity|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // fix for bug with block API, see [[phab:T68646]] if (blockoptions.expiry === 'infinity') blockoptions.expiry = 'infinite'; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinity|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> o9chtlls6r6rqwl109ks6solt8f6hcy 366011 366010 2019-05-08T16:26:04Z en>Amorymeltzer 0 Repo at 53b6bf4: Remove old fix for indef expirys that is no longer necessary, refactor some regex 366011 javascript text/javascript //<nowiki> (function($){ var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if ( Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName') ) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user' ); } }; Twinkle.block.callback = function twinkleblockCallback() { if( mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm( 'You are about to block yourself! Are you sure you want to proceed?' ) ) { return; } var Window = new Morebits.simpleWindow( 650, 530 ); // need to be verbose about who we're blocking Window.setTitle( 'Block or issue block template to ' + mw.config.get('wgRelevantUserName') ); Window.setScriptName( 'Twinkle' ); Window.addFooterLink( 'Block templates', 'Template:Uw-block/doc/Block_templates' ); Window.addFooterLink( 'Block policy', 'WP:BLOCK' ); Window.addFooterLink( 'Twinkle help', 'WP:TW/DOC#block' ); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm( Twinkle.block.callback.evaluate ); var actionfield = form.append( { type: 'field', label: 'Type of action' } ); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append( { type:'submit' } ); var result = form.render(); Window.setContent( result ); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent( 'Event' ); evt.initEvent( 'change', true, true ); result.actiontype[0].dispatchEvent( evt ); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data){ var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') return fn(); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] } ); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] } ); if (Twinkle.block.currentBlockInfo) { field_block_options.append( { type: 'hidden', name: 'reblock', value: '1' } ); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append( { type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template } ); field_template_options.append( { type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' } ); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' } ); } field_template_options.append( { type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason } ); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append( { type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] } ); } else { field_template_options.append( { type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] } ); } var $previewlink = $( '<a id="twinkleblock-preivew-link">Preview</a>' ); $previewlink.off('click').on('click', function(){ Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append( { type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] } ); field_template_options.append( { type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' } ); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $( '<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)' ); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock' : { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school' : { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy' : { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block' : { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock' : { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block' : { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock' : { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock' : { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock' : { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block' : { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk' : { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock' : { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock' : { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock' : { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock' : { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock' : { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock' : { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock' : { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock' : { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock' : { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock' : { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked' : { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous' : { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock' : { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock' : { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy' : { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ], }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === "uw-talkrevoked") return; var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : (blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true); if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) return { label: blockGroup.label, list: list }; }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) return; e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) Twinkle.block.seeAlsos.push(this.value); var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) return; var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if ( form.template_expiry.parentNode.style.display === 'none' ) { if(Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) form.expiry.value = Twinkle.block.prev_template_expiry; Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test( form.template_expiry ? form.template_expiry.value : form.expiry.value ), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) return alert('Please provide an expiry!'); if (!blockoptions.reason) return alert('Please provide a reason for the block!'); Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api( 'Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) Twinkle.block.callback.issue_template(templateoptions); }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.status.init( e.target ); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page( userTalkPage, 'User talk page modification' ); wikipedia_page.setCallbackParameters( params ); wikipedia_page.setFollowRedirect( true ); wikipedia_page.load( Twinkle.block.callback.main ); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:'+params.template; if (params.article && settings.pageParam) text += '|page=' + params.article; if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if(!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) text += '|reason=' + params.reason; if (params.disabletalk) text += '|notalk=yes'; } else { text += params.template; } if (settings.sig) text += '|sig=' + settings.sig; return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain( pageobj ) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp( '^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg' ); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec( text )) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf( '\n==' ) + 1; if ( text.length > 0 ) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test( params.expiry ); if ( Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite ) { Morebits.status.info( 'Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date' ); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if( !dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex ) { Morebits.status.info( 'Info', 'Will create a new level 2 heading for the date, as none was found for this month' ); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== "undefined" ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if ( messageData.suppressArticleInSummary !== true && params.article ) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText( text ); pageobj.setEditSummary( summary ); pageobj.setWatchlist( Twinkle.getPref('watchWarnings') ); pageobj.save(); }; })(jQuery); //</nowiki> i5ci6btf2jb7gqkj8ixtatin1lkqffk 366012 366011 2019-06-12T19:46:57Z en>Amorymeltzer 0 Repo at d404881: Greatly expand style guidelines, enforce linting rules (#657) 366012 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] }); } else { field_template_options.append({ type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-myblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 78pw1k84ad9drylcjzhf9jow2g3xiuc 366013 366012 2019-09-04T04:10:07Z en>MusikAnimal 0 Repo at 1d9a974: uw-myblock renamed to uw-socialmediablock 366013 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] }); } else { field_template_options.append({ type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-myblock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> gll6ho05m1g4bjkhcdbuy73lyob1gl5 366014 366013 2019-09-04T13:32:23Z en>Amorymeltzer 0 Repo at 65c5e3a: Fix bug causing block templates not to show up 366014 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] }); } else { field_template_options.append({ type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to contribute to the encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to contribute to the encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> stm19yyxa29t6wrbsanc2p9vztqj6m0 366015 366014 2019-11-20T17:55:51Z en>Amorymeltzer 0 Repo at 97d037f: Add Special:Block to whitelisted special pages; Correct NOTHERE label 366015 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] }); } else { field_template_options.append({ type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 4mgicwskphywdbd1avssqrxglw8h1ot 366016 366015 2019-11-28T02:18:25Z en>Amorymeltzer 0 Repo at 90b0a1d: ascii characters 366016 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] }); } else { field_template_options.append({ type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 8jp6bo5nmickgzq4ydne5xdgisvlhwa 366017 366016 2020-01-16T01:36:24Z en>Amorymeltzer 0 Tweak language around partial blocks for already-blocked users, note that blocks are currently sitewide only (#803) 366017 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) * Config directives in: [soon to be TwinkleConfig] */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user sitewide'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user sitewide with given options.', checked: true }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups() }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Prevent logged-in users from editing from this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if ($form.find('[name=actiontype][value=template]').is(':checked')) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); if (!$form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if ($form.find('[name=actiontype][value=block]').is(':checked')) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked from editing temporarily for..."' } ] }); } else { field_template_options.append({ type: 'checkbox', name: 'notalk', list: [ { label: 'Talk page access disabled', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Use this to make the block template state that the user\'s talk page access has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); } else { $form.find('fieldset[name="field_block_options"]').hide(); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); if (Twinkle.block.currentBlockInfo.partial === '') { // Partial block Morebits.status.warn(relevantUserName + ' is partially blocked', 'Submit query to convert to a sitewide block with supplied options'); } else if (Twinkle.block.currentBlockInfo.partial === undefined) { // Sitewide block Morebits.status.warn(relevantUserName + ' is already blocked', 'Submit query to reblock with supplied options'); } Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if ($form.find('[name=actiontype][value=template]').is(':checked')) { // make sure all the fields are correct based on defaults if ($form.find('[name=actiontype][value=block]').is(':checked')) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-famous': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-famous}} <!-- Username represents a famous person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a famous person, soft block', value: 'uw-ublock-famous' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(show_template) { return $.map(Twinkle.block.blockGroups, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> r5mnl92j2mpi06qgh5elm07flisc9sa 366018 366017 2020-01-24T18:05:07Z en>Amorymeltzer 0 Repo at 7b29f30: Add support for partial blocks (#813);Use server time rather than client time within load callbacks, etc 366018 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } // Used to successfully build the blocking API parameters field_block_options.append({ type: 'hidden', name: 'partial', value: partialBox }); } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', tags: true, placeholder: 'Enter pages to block user from', maximumSelectionLength: 10, // Software limitation [[phab:T202776]] language: { noResults: function() { return 'No pages entered yet'; } } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); // Reduce padding mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial stuff // blockoptions.partial handled with hidden item templateoptions.partial = $form.find('[name=actiontype][value=partial]').is(':checked'), templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += 'namespaces (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> qbu5gxswkhsv9k1moga5rykn8sfqgog 366019 366018 2020-01-24T19:00:24Z en>Amorymeltzer 0 Fix for bug in partial status 366019 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } // Used to successfully build the blocking API parameters field_block_options.append({ type: 'hidden', name: 'partial', value: partialBox }); } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', tags: true, placeholder: 'Enter pages to block user from', maximumSelectionLength: 10, // Software limitation [[phab:T202776]] language: { noResults: function() { return 'No pages entered yet'; } } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); // Reduce padding mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; blockoptions.partial = blockoptions.partial === 'true'; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial stuff // blockoptions.partial handled with hidden item templateoptions.partial = $form.find('[name=actiontype][value=partial]').is(':checked'), templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += 'namespaces (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 3k1ookd41sep2tto1do26twtft5vjmi 366020 366019 2020-01-24T19:06:51Z en>Amorymeltzer 0 Undid revision 937393019 by [[Special:Contributions/Amorymeltzer|Amorymeltzer]] ([[User talk:Amorymeltzer|talk]]) Nope 366020 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } // Used to successfully build the blocking API parameters field_block_options.append({ type: 'hidden', name: 'partial', value: partialBox }); } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', tags: true, placeholder: 'Enter pages to block user from', maximumSelectionLength: 10, // Software limitation [[phab:T202776]] language: { noResults: function() { return 'No pages entered yet'; } } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); // Reduce padding mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial stuff // blockoptions.partial handled with hidden item templateoptions.partial = $form.find('[name=actiontype][value=partial]').is(':checked'), templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += 'namespaces (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> qbu5gxswkhsv9k1moga5rykn8sfqgog 366021 366020 2020-01-24T19:23:46Z en>Amorymeltzer 0 Better fix 366021 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } // Used to successfully build the blocking API parameters field_block_options.append({ type: 'hidden', name: 'partial', value: partialBox }); } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', tags: true, placeholder: 'Enter pages to block user from', maximumSelectionLength: 10, // Software limitation [[phab:T202776]] language: { noResults: function() { return 'No pages entered yet'; } } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); // Reduce padding mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; if (!$form.find('[name=actiontype][value=partial]').is(':checked')) { delete blockoptions.partial; } templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial stuff // blockoptions.partial handled with hidden item templateoptions.partial = $form.find('[name=actiontype][value=partial]').is(':checked'), templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); console.log(blockoptions); console.log(templateoptions); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += 'namespaces (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> hwk2kos7mhvs9d723jtaqciaipxxrn9 366022 366021 2020-01-27T10:48:33Z en>Amorymeltzer 0 Repo at f1b0573: Apply bugfix for always-on partialstatus 366022 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', tags: true, placeholder: 'Enter pages to block user from', maximumSelectionLength: 10, // Software limitation [[phab:T202776]] language: { noResults: function() { return 'No pages entered yet'; } } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); // Reduce padding mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += 'namespaces (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 2xq2bew2rse1ir8r6sji6z3rhnqq44c 366023 366022 2020-01-27T18:57:52Z en>Amorymeltzer 0 Repo at f1135f6: Link pages in partial block template 366023 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', tags: true, placeholder: 'Enter pages to block user from', maximumSelectionLength: 10, // Software limitation [[phab:T202776]] language: { noResults: function() { return 'No pages entered yet'; } } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); // Reduce padding mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> dx8htiusl84b4v6ilk6sh9i8n9790d3 366024 366023 2020-01-30T17:01:09Z en>Amorymeltzer 0 Repo at fd5b702: customize "Could not load search results" text for page selector; remove stray comment; match from beginning of words only in namespace selector; create links to pages selected for partial block; use ajax lookup in partial block pages menu 366024 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partial) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partial) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Partial block', value: 'uw-pblock', selected: true } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> oh255ujtvuou7vo1uytm9fn5jqdfuig 366025 366024 2020-02-03T18:43:57Z en>Amorymeltzer 0 Repo at 65e9dea: Fix bug in block conversion display; Add additional partial blocking templates; disable partial block preset menu 366025 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = mw.config.get('wgFormattedNamespaces'); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' }, { label: 'Email harassment', value: 'uw-epblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> oritabfckvxho58cuh7gzfpk05o1nh9 366026 366025 2020-02-05T20:50:38Z en>Amorymeltzer 0 Repo at 75e6802: Don't overwrite wgFormattedNamespaces 366026 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]] with your edits' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' }, { label: 'Email harassment', value: 'uw-epblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> jixbifz3nor92pibh5ckisjyze6h7m3 366027 366026 2020-02-12T19:38:29Z en>Amorymeltzer 0 Repo at 100d502: Add tooltip to block log entry suggesting adding information; Add uw-aepblock 366027 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Arbitration enforcement', value: 'uw-aepblock' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> f5neqsw047k5gec7dx9gv6e3vpix1gy 366028 366027 2020-02-12T20:09:01Z en>Amorymeltzer 0 Repo at 2c63365: Reorganize partial block templates into two headers 366028 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsInGroup('sysop') && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot$/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 8ybzrghpjubt8hhfe4xkxlpdpe2117h 366029 366028 2020-03-04T17:39:39Z en>Amorymeltzer 0 Repo at 712686d: Add Special:Unblock link in footer; upeblock to block options (#851); Use word boundary regex to match bot usernames; Disable CU/OS options unless user has the relevant perms 366029 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); var infoStr = 'Submit query to reblock with supplied options'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ' and convert to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ' and convert to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot\b/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block api.getToken('block').then(function(token) { statusElement.status('Processing...'); blockoptions.token = token; var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }, function() { statusElement.error('Unable to fetch block token'); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Date(pageobj.getLoadTime()); var dateHeaderRegex = new RegExp('^==+\\s*(?:' + date.getUTCMonthName() + '|' + date.getUTCMonthNameAbbrev() + ')\\s+' + date.getUTCFullYear() + '\\s*==+', 'mg'); var dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += '== ' + date.getUTCMonthName() + ' ' + date.getUTCFullYear() + ' ==\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 5znb5ll7pcsn82u934ealp5q9lsbg8x 366030 366029 2020-04-25T17:27:11Z en>Amorymeltzer 0 Repo at ffe5bbed: use Morebits.date instead of native Date; Show expiry in currently blocked message; Use mw.user.tokens for the blocking token 366030 javascript text/javascript // <nowiki> (function($) { var relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var api = new mw.Api(); api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot\b/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=from certain '; if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> tm5aoz9kfhp86f56cmazs51hvph2qro 366031 366030 2020-05-13T09:38:03Z en>Amorymeltzer 0 Repo at 0b6e9f13: Fix partialblock language when block is infinite 366031 javascript text/javascript // <nowiki> (function($) { var relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var api = new mw.Api(); api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot\b/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> mydk1fx7clxz6mg222a06flxonpb02x 366032 366031 2020-05-17T10:48:18Z en>Amorymeltzer 0 Repo at 9bf18a6: Adjust edit summary for uw-vblock 366032 javascript text/javascript // <nowiki> (function($) { var relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var api = new mw.Api(); api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } else if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot\b/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 9ep02d0hmoxc97klasse53kflf9xabd 366033 366032 2020-06-13T18:03:40Z en>Amorymeltzer 0 Repo at 6a11ff8: Fix bug showing erroneous input box on a reblock form initialization; Check if the user was blocked while filling out the form 366033 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot\b/i) > 0) { data.autoblock = false; } $(form.field_block_options).find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.prev_block_reason = null; Twinkle.block.prev_article = null; Twinkle.block.prev_reason = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 4y5md33oxviyvb65ezohfdms0laiddi 366034 366033 2020-06-29T14:35:29Z en>Amorymeltzer 0 Repo at b3b2e20: fix IE bug; remove unused variables 366034 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; relevantUserName = Twinkle.block.isRegistered ? 'User:' + mw.config.get('wgRelevantUserName') : mw.config.get('wgRelevantUserName'); if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // prevent dropdown from appearing behind the dialog, just in case '.select2-container { z-index: 10000; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.isRegistered && relevantUserName.search(/bot\b/i) > 0) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; })(jQuery); // </nowiki> 6ubv1qhsktcc5b2k3dbrecixtvkndry 366035 366034 2020-07-19T15:37:24Z en>Amorymeltzer 0 Repo at 2b6f9a7: use test, not search, when we're not using the output; Fix for usergroup checking of IPs; better detect bots and turn off autoblock; Don't follow cross-namespace redirect when issuing block template; Remove 10k z-index on select2 dropdowns 366035 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; // despite this it's preferred that you use 'infinity' as the value for expiry settings.indefinite = settings.indefinite || settings.expiry === 'infinity' || settings.expiry === 'infinite' || settings.expiry === 'indefinite' || settings.expiry === 'never'; if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'indefinite'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: (/indef|infinit|never|\*|max/).test(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true, false); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = (/indef|infinit|never|\*|max/).test(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> c5fyl19lxvdm0e5j7g7fal0hczji5kt 366036 366035 2020-07-29T00:42:15Z en>Amorymeltzer 0 Repo at 3c65fa5b: Add isInfinity for whether MW will parse string as infinite, use in block 366036 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username', value: 'uw-botublock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if (!confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true, false); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.' + Twinkle.getPref('summaryAd'); pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 7hpbbnjvc8dxu4jnlmi13so3rnq50sb 366037 366036 2020-09-05T17:22:38Z en>Amorymeltzer 0 Repo at 10b4f79c: Prevent infinite blocks of IP addresses; add uw-botuhblock (#1089); Don't confirm no restrictions if email harassment is selected 366037 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true, false); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> mj9n83x997mc5up47r1e51t15z9qbb7 366038 366037 2020-09-05T17:38:01Z en>Amorymeltzer 0 Repo at b67e2c73: Don't follow user talk page redirects when issuing block templates 366038 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked]] from creating accounts for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked]] from emailing other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> qeu0p0kram285vgtmsylak6yo6hy3pl 366039 366038 2020-09-15T01:13:59Z en>Amorymeltzer 0 Repo at c35f1fd9: Clearer text for partial block summaries 366039 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'Submit query to change the block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> obkv7kbp00bzv4hmkqkj4m1z7pwn7z8 366040 366039 2020-09-29T09:56:58Z en>Amorymeltzer 0 Repo at 540dcd6a: Slightly clearer language on form submission 366040 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); field_template_options.append({ type: 'input', name: 'article', display: 'none', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', display: 'none', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', display: 'none', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', display: 'none', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (templateBox) { // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else { Twinkle.block.callback.change_template(e); } } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> l2knfoqbfqshvw6qfqhboxwqqss5gzn 366041 366040 2020-10-18T18:58:26Z en>Amorymeltzer 0 Repo at c63b1ede: Add DS topic codes for aeblock/aepblock 366041 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; if (!$(form).find('[name=actiontype][value=block]').is(':checked')) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); } else { // Only present if block && template if (form.blank_duration) { Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); Morebits.quickForm.setElementVisibility(form.article.parentNode, !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, $(form).find('[name=actiontype][value=partial]').is(':checked') && !$(form).find('[name=actiontype][value=block]').is(':checked')); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> f62bwoj6vcwglvi6ocixkty04ooeknu 366042 366041 2020-10-24T20:34:08Z en>Amorymeltzer 0 Repo at 00d4efa: Fix bug when toggling form option 366042 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration) { text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> nff5r8yysusxz2ovt1tcfxs8buhzcx3 366043 366042 2020-10-30T15:25:50Z en>Amorymeltzer 0 Repo at 451b991: Don't add expiry to template if it's a date, not a duration 366043 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn('This user has been blocked in the past', $blockloglink[0]); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); // list=blocks without bkprops (as we do in fetchUerInfo) // returns partial: '' if the user is partially blocked var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indef)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } Morebits.status.warn(statusStr, infoStr); Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // make sure all the fields are correct based on defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var text = pageobj.getPageText(), params = pageobj.getCallbackParameters(), messageData = params.messageData, date = new Morebits.date(pageobj.getLoadTime()); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> k3iagiqkkoipbo8ug4a1orw6djom54m 366044 366043 2020-11-21T15:34:37Z en>Amorymeltzer 0 Repo at 4cba4ef: Align current/log displays; Append warning to an existing section, even if it's not the last; Favor current block information if present 366044 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preivew-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> ru8pbcxwytkplaysk8lbpjtxkwbk9p6 366045 366044 2020-12-10T11:34:28Z en>Amorymeltzer 0 Repo at 5f68c5c: Add Horn of Africa DS topic; Fix typo in preview link 366045 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { 'action': 'query', 'format': 'json', 'list': 'allpages', 'apfrom': title.title, 'apnamespace': title.namespace, 'aplimit': '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)'); Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> mxcijnzmuawsu5ywj8fc4sblofb6khd 366046 366045 2020-12-16T10:56:46Z en>Amorymeltzer 0 Repo at f050fa9: Apply; Show expiry/timestamp for last action (if not currently restricted) 366046 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> hrnwao19uh1anwslw13cwrpteqf86wf 366047 366046 2021-01-17T22:28:05Z en>Amorymeltzer 0 Repo at f9aea90: Adjust default expiry for copyright blocks to be indefinite for registered users 366047 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1932': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 88kocha0jkiwpw651xwcf94uwdbnk8s 366048 366047 2021-01-20T00:51:28Z en>Amorymeltzer 0 Repo at d2068b3: AP2 amended to post-1992 366048 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 2cnagwhalvvhlcn4wajn90nl7f21ri1 366049 366048 2021-01-31T16:09:40Z en>Amorymeltzer 0 Repo at 97b8143: Add footerlink to Twinkle talk page 366049 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new level 2 heading for the date'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new level 2 heading for the date, as none was found for this month'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 4h31bfsx0lgy8jpcdjsbjcw33ppy2oo 366050 366049 2021-02-08T18:42:14Z en>Amorymeltzer 0 Repo at f27c7b2: Change reference to "level 2 heading" to "talk page section" 366050 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { // should show on Contributions or Block pages, anywhere there's a relevant user if (Morebits.userIsSysop && mw.config.get('wgRelevantUserName')) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (mw.config.get('wgRelevantUserName') === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + mw.config.get('wgRelevantUserName')); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', checked: true } ] }); form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (Twinkle.block.currentBlockInfo) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); if (Twinkle.block.currentBlockInfo) { Window.addFooterLink('Unblock this user', 'Special:Unblock/' + mw.config.get('wgRelevantUserName'), true); } // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { api.get({ format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, bkusers: mw.config.get('wgRelevantUserName'), bkprop: 'expiry|reason|flags|restrictions', ususers: mw.config.get('wgRelevantUserName'), usprop: 'groupmemberships', letitle: 'User:' + mw.config.get('wgRelevantUserName') }) .then(function(data) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { relevantUserName = 'User:' + mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { relevantUserName = mw.config.get('wgRelevantUserName'); Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; Twinkle.block.currentBlockInfo = blockinfo; } Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var partial = $form.find('[name=actiontype][value=partial]'); var partialBox = partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset if (Twinkle.block.currentBlockInfo) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; var prior = { label: 'Prior block', // value not a valid template selection, chosen below by setting templateName list: [{ label: 'Prior block settings', value: 'prior', selected: true }] }; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (Twinkle.block.currentBlockInfo) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for aeblock and aepblock, toggled in change_template field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } if (Twinkle.block.currentBlockInfo) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: mw.config.get('wgRelevantUserName'), type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This user has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, // Placeholder for when we add support for rangeblocks // 'rangeblock' : { // reason: '{{rangeblock}}', // nocreate: true, // nonstandard: true, // forAnonOnly: true, // sig: '~~~~' // }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-account', value: 'checkuserblock-account', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide', disabled: !Morebits.userIsInGroup('checkuser') }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock', disabled: !Morebits.userIsInGroup('oversight') }, // { label: 'rangeblock', value: 'rangeblock' }, // placeholder for when we add support for rangeblocks { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { // only show uw-talkrevoked if reblocking if (!Twinkle.block.currentBlockInfo && blockPreset.value === 'uw-talkrevoked') { return; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var key = e.target.form.preset.value; if (!key) { return; } e.target.form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); Twinkle.block.callback.change_template(e); }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + mw.config.get('wgRelevantUserName')); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = mw.config.get('wgRelevantUserName'); // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ api.get({ format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user, bkusers: blockoptions.user }).then(function(data) { var block = data.query.blocks[0]; var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + mw.config.get('wgRelevantUserName') + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> icba1k4ezs2e68ym88fxp1m2kl63x9h 366051 366050 2021-02-18T17:10:11Z en>Amorymeltzer 0 Repo at ab124a59: Only select {{rangeblock}} for IPv6 ranges larger than /64; Leave template on the page of the current IP even when blocking a /64; Hide rather than disable CUOS options; Add Morebits.ip.isRange to simplify use of mw.util.isIPAddress; Reorganize IP utilities into Morebits.ip object; Add checkbox to just block the /64; Enable for IP ranges, improve handling of rangeblocks; Show the length of the last block if it expired naturally 366051 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: relevantUserName !== mw.config.get('wgRelevantUserName'), // In case the user closes and reopens the form label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'GamerGate or any gender-related dispute or controversy': { code: 'gg', page: 'Wikipedia:Arbitration/Requests/Case/GamerGate' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 50d5rc0w3z5huk82e5hkb6sjm7w03jo 366052 366051 2021-02-23T01:06:43Z en>Amorymeltzer 0 Repo at 387720d6: GamerGate arb case moved to Gender and sexuality 366052 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: relevantUserName !== mw.config.get('wgRelevantUserName'), // In case the user closes and reopens the form label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'Gender-related dispute or controversy and associated people (includes GamerGate)': { code: 'gas', page: 'Wikipedia:Arbitration/Requests/Case/Gender and sexuality' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> fxc98ekcy3zy394yqxr3t9ylwar3t4s 366053 366052 2021-02-23T15:43:35Z en>Amorymeltzer 0 Repo at 659bfc5e: Kurds and Kurdistan DS topic 366053 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: relevantUserName !== mw.config.get('wgRelevantUserName'), // In case the user closes and reopens the form label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Deliberately triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for making disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'Gender-related dispute or controversy and associated people (includes GamerGate)': { code: 'gas', page: 'Wikipedia:Arbitration/Requests/Case/Gender and sexuality' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Kurds and Kurdistan': { code: 'kurd', page: 'Wikipedia:Arbitration/Requests/Case/Kurds and Kurdistan' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 6zkyxyh7xzpcusswnrntd3yfdltijfu 366054 366053 2021-02-24T18:51:26Z en>Amorymeltzer 0 Repo at 90379aab: Match uhblock-double language to ipbreason; Reword uw-efblock reason to say 'Repeatedly triggering' rather than 'Deliberately triggering' 366054 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: relevantUserName !== mw.config.get('wgRelevantUserName'), // In case the user closes and reopens the form label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.actiontype[0].dispatchEvent(evt); }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking: ', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..." ', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'Gender-related dispute or controversy and associated people (includes GamerGate)': { code: 'gas', page: 'Wikipedia:Arbitration/Requests/Case/Gender and sexuality' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Kurds and Kurdistan': { code: 'kurd', page: 'Wikipedia:Arbitration/Requests/Case/Kurds and Kurdistan' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> ffr8hwfyiissgi8jwv17cf6w2zf0b6i 366055 366054 2021-09-08T15:53:41Z en>MusikAnimal 0 Repo at 66e1c54: Add option to default to blocking the /64, off by default (#1352); remove trailing space in input field labels 366055 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason, list: $.map(Twinkle.block.dsinfo, function(info, label) { return {label: label, value: info.code}; }) }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; // Codes and links for Discretionary Sanctions, see [[Template:Ds/topics]] // Used for uw-ae(p)block Twinkle.block.dsinfo = { '': { code: '' }, 'Abortion': { code: 'ab', page: 'Wikipedia:Arbitration/Requests/Case/Abortion' }, 'American politics post-1992': { code: 'ap', page: 'Wikipedia:Arbitration/Requests/Case/American politics 2' }, 'Ancient Egyptian race controversy': { code: 'aerc', page: 'Wikipedia:Requests for arbitration/Ancient Egyptian race controversy' }, 'Arab-Israeli conflict': { code: 'a-i', page: 'Wikipedia:Arbitration/Index/Palestine-Israel articles' }, 'Armenia, Azerbaijan, or related conflicts': { code: 'a-a', page: 'Wikipedia:Requests for arbitration/Armenia-Azerbaijan 2' }, 'Biographies of Living Persons (BLPs)': { code: 'blp', page: 'Wikipedia:Requests for arbitration/Editing of Biographies of Living Persons' }, 'Climate change': { code: 'cc', page: 'Wikipedia:Arbitration/Requests/Case/Climate change' }, 'Complementary and alternative medicine': { code: 'com', page: 'Wikipedia:Arbitration/Requests/Case/Acupuncture' }, 'Eastern Europe or the Balkans': { code: 'e-e', page: 'Wikipedia:Requests for arbitration/Eastern Europe' }, 'Electronic cigarettes': { code: 'ecig', page: 'Wikipedia:Arbitration/Requests/Case/Editor conduct in e-cigs articles' }, 'Falun Gong': { code: 'fg', page: 'Wikipedia:Requests for arbitration/Falun Gong' }, 'Gender-related dispute or controversy and associated people (includes GamerGate)': { code: 'gas', page: 'Wikipedia:Arbitration/Requests/Case/Gender and sexuality' }, 'Genetically modified organisms (GMO)': { code: 'gmo', page: 'Wikipedia:Arbitration/Requests/Case/Genetically modified organisms' }, 'Gun control': { code: 'gc', page: 'Wikipedia:Arbitration/Requests/Case/Gun control' }, 'Horn of Africa (Ethiopia, Somalia, Eritrea, Djibouti)': { code: 'horn', page: 'Wikipedia:Arbitration/Requests/Case/Horn of Africa' }, 'India, Pakistan, and Afghanistan': { code: 'ipa', page: 'Wikipedia:Requests for arbitration/India-Pakistan' }, 'Infoboxes': { code: 'cid', page: 'Wikipedia:Arbitration/Requests/Case/Civility in infobox discussions' }, 'Kurds and Kurdistan': { code: 'kurd', page: 'Wikipedia:Arbitration/Requests/Case/Kurds and Kurdistan' }, 'Landmark Worldwide': { code: 'lw', page: 'Wikipedia:Arbitration/Requests/Case/Landmark Worldwide' }, 'Liancourt Rocks': { code: 'lr', page: 'Wikipedia:Requests for arbitration/Liancourt Rocks' }, 'Manual of Style and article titles': { code: 'mos', page: 'Wikipedia:Arbitration/Requests/Case/Article titles and capitalisation' }, 'Muhammad': { code: 'muh-im', page: 'Wikipedia:Arbitration/Requests/Case/Muhammad images' }, 'Pharmaceutical drug prices (medicine)': { code: 'med', page: 'Wikipedia:Arbitration/Requests/Case/Medicine' }, 'Prem Rawat': { code: 'pr', page: 'Wikipedia:Requests for arbitration/Prem Rawat' }, 'Pseudoscience and fringe science': { code: 'ps', page: 'Wikipedia:Requests for arbitration/Pseudoscience' }, 'Race/ethnicity and human abilities, behaviour, and intelligence': { code: 'r-i', page: 'Wikipedia:Arbitration/Requests/Case/Race and intelligence' }, 'Scientology': { code: 'sci', page: 'Wikipedia:Requests for arbitration/Scientology' }, 'Senkaku Islands dispute': { code: 'sen', page: 'Wikipedia:Arbitration/Requests/Case/Senkaku Islands' }, 'September 11 attacks': { code: '9/11', page: 'Wikipedia:Requests for arbitration/September 11 conspiracy theories' }, 'Shakespeare authorship question': { code: 'saq', page: 'Wikipedia:Arbitration/Requests/Case/Shakespeare authorship question' }, 'Transcendental Meditation movement': { code: 'tm', page: 'Wikipedia:Arbitration/Requests/Case/Transcendental Meditation movement' }, 'The Troubles': { code: 'tt', page: 'Wikipedia:Requests for arbitration/The Troubles' }, 'Waldorf education': { code: 'we', page: 'Wikipedia:Requests for arbitration/Waldorf education' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsReason = Twinkle.block.dsinfo[this.options[this.selectedIndex].label].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 43winxwt4tyiv3e7wqajf4dq4qxsmx0 366056 366055 2022-02-04T06:00:46Z en>MusikAnimal 0 Repo at dd76177: read discretionary sanctions information from a wiki page (#1479) 366056 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> r7kha718mlzv90rdhjc7nf4czklq1u0 366057 366056 2022-02-26T19:44:26Z en>Izno 0 hotfix, see https://github.com/wikimedia-gadgets/twinkle/pull/1526 for full fix 366057 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 5anpwlb3a0xtvo2lzb23qcyf9dwnfub 366058 366057 2022-03-10T20:04:26Z en>MusikAnimal 0 Repo at dd76177: fix {{OversightBlock}} not showing as option 366058 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('oversight')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> r7kha718mlzv90rdhjc7nf4czklq1u0 366059 366058 2022-03-10T20:05:42Z en>MusikAnimal 0 Repo at f8e5769: fix {{OversightBlock}} not showing as option (#1526) 366059 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Vandalism-only account|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:VOA|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> 5anpwlb3a0xtvo2lzb23qcyf9dwnfub 366060 366059 2023-01-17T17:15:19Z en>MusikAnimal 0 Repo at 49207b0: "Vandalism-only account": Link to policy rather than essay (#1658) 366060 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict = blockSettings.forRegisteredOnly ? Twinkle.block.isRegistered : blockSettings.forAnonOnly ? !Twinkle.block.isRegistered : true; if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> spknjpwp58k8s5avybe5x7e4pmzxo66 366061 366060 2023-02-26T06:26:55Z en>MusikAnimal 0 Repo at dff0416: disallow nested ternaries (#1695) 366061 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]].' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forAnonOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> gpzy4ma086samxcg9oew8f45wobk2rj 366062 366061 2023-07-19T08:57:47Z en>Novem Linguae 0 Repo at fe1535b: fix extra period in {{uw-botuhblock}} edit summary (#1774) 366062 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forAnonOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for anonymous users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forAnonOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forAnonOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forAnonOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forAnonOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forAnonOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forAnonOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forAnonOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forAnonOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> rh36s5pf8qec6unrouajfzjh4xz0tww 366063 366062 2024-10-16T09:16:07Z en>Novem Linguae 0 Repo at 3425932: See https://en.wikipedia.org/wiki/Template_talk:Welcome-unregistered-constructive#Requested_move_29_December_2023 (#1941) 366063 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> meq3k2mokvcwah6eqmncl3fqs31y6n0 366064 366063 2024-11-10T18:05:25Z en>SD0001 0 Repo at 4a07910: Add dark mode compatibility to Twinkle (#2023) 366064 javascript text/javascript // <nowiki> (function($) { var api = new mw.Api(), relevantUserName, blockedUserName; var menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; var Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); var form = new Morebits.quickForm(Twinkle.block.callback.evaluate); var actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ var sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { var block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(function() { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched var evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { var blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map(function(e) { return e.group; }).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { var query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then(function(data) { Twinkle.block.processUserInfo(data, fn); }, function(msg) { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach(function(el) { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { var $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 var priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) var originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content var regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. var titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link var unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { var field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter var blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); var templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); var $partial = $form.find('[name=actiontype][value=partial]'); var partialBox = $partial.is(':checked'); var blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset var prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some(function(bg) { return bg.label === prior.label; })) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter(function(bg) { return bg.label !== prior.label; }); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); var ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, function(number, name) { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } var blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then(function(dsinfo) { var $select = $('[name="dstopic"]'); var $options = $.map(dsinfo, function (value, key) { return $('<option>').val(value.code).text(key).prop('label', key); }); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here var dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } var $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', function() { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } var oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { var title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map(function(page) { var title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block var sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); var statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range var $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } var infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { var $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { var lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, function(preset, settings) { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, function(blockGroup) { var list = $.map(blockGroup.list, function(blockPreset) { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } var blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; var registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { var templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { var form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { var expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { var reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter(function(el) { return el !== this.value; }.bind(this)); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } var seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { var reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then(function(dsinfo) { var sanctionCode = this.selectedIndex; var sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }.bind(this)); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { var form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each(function(i, el) { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } var check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { var $pageSelect = $(form).find('[name=pagerestrictions]'); var $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { var pages = data.restrictions.pages.map(function(pr) { return pr.title; }); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach(function(page) { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { var newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { var form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; var blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); var partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); var templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { var params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; var templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { var $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); var statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ var query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then(function(data) { var block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } var logevents = data.query.logevents[0]; var logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { var message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } var logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { var expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); var mbApi = new Morebits.wiki.api('Executing block', blockoptions, function() { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } }); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) var userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); var params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; var wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { var text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { var makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } var last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map(function(p) { return '[[:' + p + ']]'; })); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. var namespaceNames = params.namespacerestrictions.map(function(id) { return menuFormattedNamespaces[id]; }); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { var params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); var dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. var lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary var summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); })(jQuery); // </nowiki> iaszg3njo15kploxdu9ch1f97d0wpj7 366065 366064 2024-11-23T17:26:27Z en>Novem Linguae 0 Repo at 3da72fd: apply autofixes 1 (#2058) 366065 javascript text/javascript // <nowiki> (function($) { let api = new mw.Api(), relevantUserName, blockedUserName; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; const Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.quickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { 'anonblock': { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, 'colocationwebhost': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, 'oversightblock': { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, 'spamblacklistblock': { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, 'rangeblock': { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, 'tor': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, 'webhostblock': { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { let form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { let $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); const statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { let params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); let dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }(jQuery)); // </nowiki> o34lul1ope168gy4kbz8tsjthb7hwio 366066 366065 2024-11-26T22:27:47Z en>Novem Linguae 0 Repo at 001f236: simplify IIFEs (#2074); apply autofixes 3 (#2079); apply autofixes 2 (#2069) 366066 javascript text/javascript // <nowiki> (function() { let api = new mw.Api(), relevantUserName, blockedUserName; const menuFormattedNamespaces = Object.assign({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; const Window = new Morebits.simpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.quickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true, unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false, unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.status.init($('div[name="currentblock"] span').last()[0]); Morebits.status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.quickForm.element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.quickForm.element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.quickForm.element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.quickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { let form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.quickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.quickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.quickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.quickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.quickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.quickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.quickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.quickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.quickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { let $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); const statusElement = new Morebits.status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Object.assign(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { let params = pageobj.getCallbackParameters(), date = new Morebits.date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); let dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 68y0nfzhns9kr3qnfa4i6ar829ryvse 366067 366066 2024-11-29T23:27:00Z en>Novem Linguae 0 Repo at e7d4c6c: fix some no-jquery/no-extend autofixes (#2088); capitalize Morebit class names (#2087); fix linter error no-sequences (#2083) 366067 javascript text/javascript // <nowiki> (function() { let api = new mw.Api(), relevantUserName, blockedUserName; const menuFormattedNamespaces = Object.assign({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; const Window = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { let form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { let $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Object.assign(formData || {}, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { let params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); let dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 8r8tiww8ikfdgscl24dzx763o23mkhv 366068 366067 2024-12-07T22:29:50Z en>Novem Linguae 0 Repo at 5152750: revert $.extend autofixes (#2107) 366068 javascript text/javascript // <nowiki> (function() { let api = new mw.Api(), relevantUserName, blockedUserName; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; const Window = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0], userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options, $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) ' + 'see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { let form = e.target.form, expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { let $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'), blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = $.extend(formData, { messageData: Twinkle.block.blockPresetsInfo[formData.template], reason: Twinkle.block.field_template_options.block_reason, disabletalk: Twinkle.block.field_template_options.notalk, noemail: Twinkle.block.field_template_options.noemail_template, nocreate: Twinkle.block.field_template_options.nocreate_template }); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{', settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { let params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData, text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); let dateHeaderRegex = date.monthHeaderRegex(), dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> n6iwlthqcdmhy8zya3sdcw9xeqen8f9 366069 366068 2024-12-10T11:37:53Z en>Novem Linguae 0 Repo at 04d11bb: add regression test (#2110); fix no-useless-concat (#2100); fix prefer-const (#2099) 366069 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; const Window = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).indexOf('bot') !== -1; } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.indexOf('{{') !== -1) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && blockoptions.namespacerestrictions.indexOf('3') === -1) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> fszmchk7hvq4fokdg5kfpn1u1sqqtc5 366070 366069 2025-01-24T23:50:47Z en>Novem Linguae 0 Repo at ac3c1e3: replace es-x/no-array-prototype-includes with unicorn/prefer-includes (#2125) 366070 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; const Window = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking Window.setTitle('Block or issue block template to ' + relevantUserName); Window.setScriptName('Twinkle'); Window.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); Window.addFooterLink('Block policy', 'WP:BLOCK'); Window.addFooterLink('Block prefs', 'WP:TW/PREF#block'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); Window.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked Window.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); Window.setContent(result); Window.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> dpv5lg7c8arhpi0jvgfliy91q3wstv2 366071 366070 2025-06-09T18:47:13Z en>MusikAnimal 0 deploy #2180: soft redirect to Special:Block for multi-blocked targets 366071 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { disabletalk: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 1cmu11wbf2e6pdq9gfoepif2ed47wpi 366072 366071 2025-10-19T21:56:08Z en>Novem Linguae 0 Repo at 29d9225: 366072 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forUnregisteredOnly: <show block option in the interface only if the relevant user is an IP> * forRegisteredOnly: <show block option in the interface only if the relevant user is registered> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forUnregisteredOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forUnregisteredOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forUnregisteredOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forUnregisteredOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forUnregisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnregisteredOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forUnregisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let registrationRestrict; if (blockSettings.forRegisteredOnly) { registrationRestrict = Twinkle.block.isRegistered; } else if (blockSettings.forUnregisteredOnly) { registrationRestrict = !Twinkle.block.isRegistered; } else { registrationRestrict = true; } if (!(blockSettings.templateName && show_template) && registrationRestrict) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> prcwwbntu2ut4q8byj6nw92fl4lys7h 366073 366072 2025-11-07T17:53:16Z en>Novem Linguae 0 Repo at b797ead: add "forUnnamedOnly", then use it for "uw-ipevadeblock" (#2232) 366073 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forUnnamedOnly: <show block option in the interface only if the relevant user is an IP or a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forUnnamedOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion', value: 'uw-ipevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; // for IPs and temporary accounts } else if (blockSettings.forUnnamedOnly) { allowedUserType = !Twinkle.block.isRegistered || mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); } else { allowedUserType = true; } if (!(blockSettings.templateName && show_template) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 04433e4ozt63yle08gn8w2m56jly2dm 366074 366073 2025-11-18T11:51:57Z en>Novem Linguae 0 Repo at ee260ff: add {{uw-tempevadeblock}} (with autoblock) and forTempAccountsOnly (#2236) 366074 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let field_preset, field_template_options, field_block_options; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { field_preset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); field_preset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); field_block_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); field_block_options.append({ type: 'div', name: 'currentblock', label: ' ' }); field_block_options.append({ type: 'div', name: 'hasblocklog', label: ' ' }); field_block_options.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); field_block_options.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block field_block_options.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = field_block_options.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); field_block_options.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); field_block_options.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); field_block_options.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); field_block_options.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); field_block_options.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { field_block_options.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { field_template_options = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); field_template_options.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template field_template_options.append(dsSelectSettings); field_template_options.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking field_template_options.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { field_template_options.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } field_template_options.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { field_template_options.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { field_template_options.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); field_template_options.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); field_template_options.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (field_preset) { // Only visible for arbitration enforcement, toggled in change_preset field_preset.append(dsSelectSettings); } let oldfield; if (field_preset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(field_preset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (field_block_options) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(field_block_options.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (field_template_options) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(field_template_options.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion - Temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, show_template) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && show_template) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (show_template ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipedia_page = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> n8qaqxlx66fh1txz3vnl4lhr4d45ezf 366075 366074 2025-11-25T20:41:32Z en>Novem Linguae 0 Repo at 58a0dd8: switch to camelcase, part 1 (#2245) 366075 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log or deleted contributions played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion - Temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + Twinkle.block.seeAlsos.join(' and ') + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = Twinkle.block.seeAlsos.join(' and '); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> g75zm694880ctcyiwwc6lqq0ie8gbh6 366076 366075 2025-12-06T08:03:33Z en>SD0001 0 Repo at 49bb459: Move all non-test source files to a new src directory (#2254); add checkbox for related TAs to seeAlsos (#2250) 366076 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' }, { checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }, { checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' } ]; if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log, deleted contributions or related temporary accounts played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { fieldBlockOptions.append({ type: 'checkbox', name: 'related_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Related temporary accounts', checked: false, value: 'related temporary accounts' } ] }); } // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'anonblock', value: 'anonblock' }, { label: 'anonblock - likely a school', value: 'anonblock - school' }, { label: 'school block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) - IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) - indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion - IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion - Temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'checkuserblock-account', value: 'checkuserblock-account' }, { label: 'checkuserblock-wide', value: 'checkuserblock-wide' }, { label: 'colocationwebhost', value: 'colocationwebhost' }, { label: 'oversightblock', value: 'oversightblock' }, { label: 'rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'spamblacklistblock', value: 'spamblacklistblock' }, { label: 'tor', value: 'tor' }, { label: 'webhostblock', value: 'webhostblock' }, { label: 'zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) - indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts - indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const joinEnum = function(e) { if (e.length >= 3) { return e.slice(0, -1).join(', ') + ' and ' + e[e.length - 1]; } else { return e.join(' and '); } }; const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + joinEnum(Twinkle.block.seeAlsos) + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = joinEnum(Twinkle.block.seeAlsos); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> e1n3i1zzmfyrdiiou5amwymyfcxatvb 366077 366076 2026-02-11T19:56:16Z en>SD0001 0 Repo at 3ea71e1: consistent capitalization/style in block options (#2314); hide the email option for TAs and IPs (#2312) 366077 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. $(blockWindow.content).dialog('widget').find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' } ]; if (Twinkle.block.isRegistered && !mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { blockoptions.push({ checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' }); if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log, deleted contributions or related temporary accounts played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { fieldBlockOptions.append({ type: 'checkbox', name: 'related_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Related temporary accounts', checked: false, value: 'related temporary accounts' } ] }); } // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'Anonblock', value: 'anonblock' }, { label: 'Anonblock – likely a school', value: 'anonblock - school' }, { label: 'School block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion – temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'Blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'CheckUser block – account', value: 'checkuserblock-account' }, { label: 'CheckUser block – wide', value: 'checkuserblock-wide' }, { label: 'Colocation webhost', value: 'colocationwebhost' }, { label: 'Oversight block', value: 'oversightblock' }, { label: 'Rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'Spam blacklist block', value: 'spamblacklistblock' }, { label: 'Tor', value: 'tor' }, { label: 'Webhost block', value: 'webhostblock' }, { label: 'Zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) – indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts – indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const joinEnum = function(e) { if (e.length >= 3) { return e.slice(0, -1).join(', ') + ' and ' + e[e.length - 1]; } else { return e.join(' and '); } }; const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + joinEnum(Twinkle.block.seeAlsos) + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = joinEnum(Twinkle.block.seeAlsos); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> pvw0ugz96u7cv8axuyb7rozwo9n36x2 366078 366077 2026-02-14T08:22:10Z en>SD0001 0 Repo at d88f5c4: port SimpleWindow from jQuery UI to plain JS (#2309) 366078 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. blockWindow.$dialog.find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.ui-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' } ]; if (Twinkle.block.isRegistered && !mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { blockoptions.push({ checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' }); if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log, deleted contributions or related temporary accounts played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { fieldBlockOptions.append({ type: 'checkbox', name: 'related_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Related temporary accounts', checked: false, value: 'related temporary accounts' } ] }); } // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'Anonblock', value: 'anonblock' }, { label: 'Anonblock – likely a school', value: 'anonblock - school' }, { label: 'School block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion – temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'Blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'CheckUser block – account', value: 'checkuserblock-account' }, { label: 'CheckUser block – wide', value: 'checkuserblock-wide' }, { label: 'Colocation webhost', value: 'colocationwebhost' }, { label: 'Oversight block', value: 'oversightblock' }, { label: 'Rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'Spam blacklist block', value: 'spamblacklistblock' }, { label: 'Tor', value: 'tor' }, { label: 'Webhost block', value: 'webhostblock' }, { label: 'Zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) – indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts – indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const joinEnum = function(e) { if (e.length >= 3) { return e.slice(0, -1).join(', ') + ' and ' + e[e.length - 1]; } else { return e.join(' and '); } }; const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + joinEnum(Twinkle.block.seeAlsos) + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = joinEnum(Twinkle.block.seeAlsos); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> m08n7eembjxdvnsokiudq7vsuw0ixl7 366079 366078 2026-02-15T19:11:45Z en>SD0001 0 Repo at b6d05ba: fix reference to titlebar while regenerating form for /64 ranges (#2320) 366079 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName, true); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. blockWindow.$dialog.find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'); // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle` or // `dialog('option', 'title')`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = document.querySelector('.morebits-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = document.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' } ]; if (Twinkle.block.isRegistered && !mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { blockoptions.push({ checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' }); if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log, deleted contributions or related temporary accounts played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { fieldBlockOptions.append({ type: 'checkbox', name: 'related_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Related temporary accounts', checked: false, value: 'related temporary accounts' } ] }); } // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [ $previewlink[0] ] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'Anonblock', value: 'anonblock' }, { label: 'Anonblock – likely a school', value: 'anonblock - school' }, { label: 'School block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion – temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'Blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'CheckUser block – account', value: 'checkuserblock-account' }, { label: 'CheckUser block – wide', value: 'checkuserblock-wide' }, { label: 'Colocation webhost', value: 'colocationwebhost' }, { label: 'Oversight block', value: 'oversightblock' }, { label: 'Rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'Spam blacklist block', value: 'spamblacklistblock' }, { label: 'Tor', value: 'tor' }, { label: 'Webhost block', value: 'webhostblock' }, { label: 'Zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) – indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts – indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const joinEnum = function(e) { if (e.length >= 3) { return e.slice(0, -1).join(', ') + ' and ' + e[e.length - 1]; } else { return e.join(' and '); } }; const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + joinEnum(Twinkle.block.seeAlsos) + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = joinEnum(Twinkle.block.seeAlsos); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function (array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 4lhnu1z53oscwzstknyui6jbxpc4lar 366080 355500 2026-06-24T06:31:18Z en>Novem Linguae 0 Repo at f08cefa: add rules for no space inside parentheses (#2341) 366080 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. blockWindow.$dialog.find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = blockWindow.$dialog.find('.morebits-dialog-footerlinks a')[0]; if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'), dialog = $form.closest('.morebits-dialog')[0]; // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = dialog.querySelector('.morebits-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = dialog.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' } ]; if (Twinkle.block.isRegistered && !mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { blockoptions.push({ checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' }); if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log, deleted contributions or related temporary accounts played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { fieldBlockOptions.append({ type: 'checkbox', name: 'related_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Related temporary accounts', checked: false, value: 'related temporary accounts' } ] }); } // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [$previewlink[0]] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'Anonblock', value: 'anonblock' }, { label: 'Anonblock – likely a school', value: 'anonblock - school' }, { label: 'School block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion – temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'Blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'CheckUser block – account', value: 'checkuserblock-account' }, { label: 'CheckUser block – wide', value: 'checkuserblock-wide' }, { label: 'Colocation webhost', value: 'colocationwebhost' }, { label: 'Oversight block', value: 'oversightblock' }, { label: 'Rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'Spam blacklist block', value: 'spamblacklistblock' }, { label: 'Tor', value: 'tor' }, { label: 'Webhost block', value: 'webhostblock' }, { label: 'Zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) – indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts – indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const joinEnum = function(e) { if (e.length >= 3) { return e.slice(0, -1).join(', ') + ' and ' + e[e.length - 1]; } else { return e.join(' and '); } }; const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + joinEnum(Twinkle.block.seeAlsos) + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = joinEnum(Twinkle.block.seeAlsos); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function(array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 9jebteo6i0o0577azgtcyrsdystfm9e 366081 366080 2026-08-20T12:31:51Z ChiK 1136 ೯೩ revisions imported from [[:en:MediaWiki:Gadget-twinkleblock.js]] 366080 javascript text/javascript // <nowiki> (function() { const api = new mw.Api(); let relevantUserName, blockedUserName, blockWindow; const menuFormattedNamespaces = $.extend({}, mw.config.get('wgFormattedNamespaces')); menuFormattedNamespaces[0] = '(Article)'; /* **************************************** *** twinkleblock.js: Block module **************************************** * Mode of invocation: Tab ("Block") * Active on: Any page with relevant user name (userspace, contribs, etc.) */ Twinkle.block = function twinkleblock() { relevantUserName = mw.config.get('wgRelevantUserName'); // should show on Contributions or Block pages, anywhere there's a relevant user // Ignore ranges wider than the CIDR limit if (Morebits.userIsSysop && relevantUserName && (!Morebits.ip.isRange(relevantUserName) || Morebits.ip.validCIDR(relevantUserName))) { Twinkle.addPortletLink(Twinkle.block.callback, 'Block', 'tw-block', 'Block relevant user'); } }; Twinkle.block.callback = function twinkleblockCallback() { if (relevantUserName === mw.config.get('wgUserName') && !confirm('You are about to block yourself! Are you sure you want to proceed?')) { return; } Twinkle.block.currentBlockInfo = undefined; Twinkle.block.field_block_options = {}; Twinkle.block.field_template_options = {}; blockWindow = new Morebits.SimpleWindow(650, 530); // need to be verbose about who we're blocking blockWindow.setTitle('Block or issue block template to ' + relevantUserName); blockWindow.setScriptName('Twinkle'); // Always added, hidden later if actual user not blocked blockWindow.addFooterLink('Unblock this user', 'Special:Unblock/' + relevantUserName); blockWindow.addFooterLink('Block templates', 'Template:Uw-block/doc/Block_templates'); blockWindow.addFooterLink('Block policy', 'WP:BLOCK'); blockWindow.addFooterLink('Block prefs', 'WP:TW/PREF#block'); blockWindow.addFooterLink('Twinkle help', 'WP:TW/DOC#block'); blockWindow.addFooterLink('Give feedback', 'WT:TW'); const form = new Morebits.QuickForm(Twinkle.block.callback.evaluate); const actionfield = form.append({ type: 'field', label: 'Type of action' }); actionfield.append({ type: 'checkbox', name: 'actiontype', event: Twinkle.block.callback.change_action, list: [ { label: 'Block user', value: 'block', tooltip: 'Block the relevant user with the given options. If partial block is unchecked, this will be a sitewide block.', checked: true }, { label: 'Partial block', value: 'partial', tooltip: 'Enable partial blocks and partial block templates.', checked: Twinkle.getPref('defaultToPartialBlocks') // Overridden if already blocked }, { label: 'Add block template to user talk page', value: 'template', tooltip: 'If the blocking admin forgot to issue a block template, or you have just blocked the user without templating them, you can use this to issue the appropriate template. Check the partial block box for partial block templates.', // Disallow when viewing the block dialog on an IP range checked: !Morebits.ip.isRange(relevantUserName), disabled: Morebits.ip.isRange(relevantUserName) } ] }); /* Add option for IPv6 ranges smaller than /64 to upgrade to the 64 CIDR ([[WP:/64]]). This is one of the few places where we want wgRelevantUserName since this depends entirely on the original user. In theory, we shouldn't use Morebits.ip.get64 here since since we want to exclude functionally-equivalent /64s. That'd be: // if (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName'), true) && // (mw.util.isIPv6Address(mw.config.get('wgRelevantUserName')) || parseInt(mw.config.get('wgRelevantUserName').replace(/^(.+?)\/?(\d{1,3})?$/, '$2'), 10) > 64)) { In practice, though, since functionally-equivalent ranges are (mis)treated as separate by MediaWiki's logging ([[phab:T146628]]), using Morebits.ip.get64 provides a modicum of relief in thise case. */ const sixtyFour = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); if (sixtyFour && sixtyFour !== mw.config.get('wgRelevantUserName')) { const block64field = form.append({ type: 'field', label: 'Convert to /64 rangeblock', name: 'field_64' }); block64field.append({ type: 'div', style: 'margin-bottom: 0.5em', label: ['It\'s usually fine, if not better, to ', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('WP:/64') + '">just block the /64</a>')[0], ' range (', $.parseHTML('<a target="_blank" href="' + mw.util.getUrl('Special:Contributions/' + sixtyFour) + '">' + sixtyFour + '</a>)')[0], ').'] }); block64field.append({ type: 'checkbox', name: 'block64', event: Twinkle.block.callback.change_block64, list: [{ checked: Twinkle.getPref('defaultToBlock64'), label: 'Block the /64 instead', value: 'block64', tooltip: Morebits.ip.isRange(mw.config.get('wgRelevantUserName')) ? 'Will eschew leaving a template.' : 'Any template issued will go to the original IP: ' + mw.config.get('wgRelevantUserName') }] }); } form.append({ type: 'field', label: 'Preset', name: 'field_preset' }); form.append({ type: 'field', label: 'Template options', name: 'field_template_options' }); form.append({ type: 'field', label: 'Block options', name: 'field_block_options' }); form.append({ type: 'submit' }); const result = form.render(); blockWindow.setContent(result); blockWindow.display(); result.root = result; Twinkle.block.fetchUserInfo(() => { // Toggle initial partial state depending on prior block type, // will override the defaultToPartialBlocks pref if (blockedUserName === relevantUserName) { $(result).find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // clean up preset data (defaults, etc.), done exactly once, must be before Twinkle.block.callback.change_action is called Twinkle.block.transformBlockPresets(); // init the controls after user and block info have been fetched const evt = document.createEvent('Event'); evt.initEvent('change', true, true); if (result.block64 && result.block64.checked) { // Calls the same change_action event once finished result.block64.dispatchEvent(evt); } else { result.actiontype[0].dispatchEvent(evt); } }); }; // Store fetched user data, only relevant if switching IPv6 to a /64 Twinkle.block.fetchedData = {}; // Processes the data from a query response, separated from // Twinkle.block.fetchUserInfo to allow reprocessing of already-fetched data Twinkle.block.processUserInfo = function twinkleblockProcessUserInfo(data, fn) { let blockinfo = data.query.blocks[0]; // Soft redirect to Special:Block if the user is multi-blocked (#2178) if (blockinfo && data.query.blocks.length > 1) { // Remove submission buttons. blockWindow.$dialog.find('.morebits-dialog-buttons').empty(); Morebits.Status.init(blockWindow.content.querySelector('form')); Morebits.Status.warn( `This target has ${data.query.blocks.length} active blocks`, `Multiblocks is not supported by Twinkle. Use [[Special:Block/${relevantUserName}]] instead.` ); return; } const userinfo = data.query.users[0]; // If an IP is blocked *and* rangeblocked, the above finds // whichever block is more recent, not necessarily correct. // Three seems... unlikely if (data.query.blocks.length > 1 && blockinfo.user !== relevantUserName) { blockinfo = data.query.blocks[1]; } // Cache response, used when toggling /64 blocks Twinkle.block.fetchedData[userinfo.name] = data; Twinkle.block.isRegistered = !!userinfo.userid; if (Twinkle.block.isRegistered) { Twinkle.block.userIsBot = !!userinfo.groupmemberships && userinfo.groupmemberships.map((e) => e.group).includes('bot'); } else { Twinkle.block.userIsBot = false; } if (blockinfo) { // handle frustrating system of inverted boolean values blockinfo.disabletalk = blockinfo.allowusertalk === undefined; blockinfo.hardblock = blockinfo.anononly === undefined; } // will undefine if no blocks present Twinkle.block.currentBlockInfo = blockinfo; blockedUserName = Twinkle.block.currentBlockInfo && Twinkle.block.currentBlockInfo.user; // Toggle unblock link if not the user in question; always first const unblockLink = blockWindow.$dialog.find('.morebits-dialog-footerlinks a')[0]; if (blockedUserName !== relevantUserName) { unblockLink.hidden = true; unblockLink.nextSibling.hidden = true; // link+trailing bullet } else { unblockLink.hidden = false; unblockLink.nextSibling.hidden = false; // link+trailing bullet } // Semi-busted on ranges, see [[phab:T270737]] and [[phab:T146628]]. // Basically, logevents doesn't treat functionally-equivalent ranges // as equivalent, meaning any functionally-equivalent IP range is // misinterpreted by the log throughout. Without logevents // redirecting (like Special:Block does) we would need a function to // parse ranges, which is a pain. IPUtils has the code, but it'd be a // lot of cruft for one purpose. Twinkle.block.hasBlockLog = !!data.query.logevents.length; Twinkle.block.blockLog = Twinkle.block.hasBlockLog && data.query.logevents; // Used later to check if block status changed while filling out the form Twinkle.block.blockLogId = Twinkle.block.hasBlockLog ? data.query.logevents[0].logid : false; if (typeof fn === 'function') { return fn(); } }; Twinkle.block.fetchUserInfo = function twinkleblockFetchUserInfo(fn) { const query = { format: 'json', action: 'query', list: 'blocks|users|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + relevantUserName, bkprop: 'expiry|reason|flags|restrictions|range|user', ususers: relevantUserName }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(relevantUserName, true)) { query.bkip = relevantUserName; } else { query.bkusers = relevantUserName; // groupmemberships only relevant for registered users query.usprop = 'groupmemberships'; } api.get(query).then((data) => { Twinkle.block.processUserInfo(data, fn); }, (msg) => { Morebits.Status.init($('div[name="currentblock"] span').last()[0]); Morebits.Status.warn('Error fetching user info', msg); }); }; Twinkle.block.callback.saveFieldset = function twinkleblockCallbacksaveFieldset(fieldset) { Twinkle.block[$(fieldset).prop('name')] = {}; $(fieldset).serializeArray().forEach((el) => { // namespaces and pages for partial blocks are overwritten // here, but we're handling them elsewhere so that's fine Twinkle.block[$(fieldset).prop('name')][el.name] = el.value; }); }; Twinkle.block.callback.change_block64 = function twinkleblockCallbackChangeBlock64(e) { const $form = $(e.target.form), $block64 = $form.find('[name=block64]'), dialog = $form.closest('.morebits-dialog')[0]; // Show/hide block64 button // Single IPv6, or IPv6 range smaller than a /64 const priorName = relevantUserName; if ($block64.is(':checked')) { relevantUserName = Morebits.ip.get64(mw.config.get('wgRelevantUserName')); } else { relevantUserName = mw.config.get('wgRelevantUserName'); } // No templates for ranges, but if the original user is a single IP, offer the option // (done separately in Twinkle.block.callback.issue_template) const originalIsRange = Morebits.ip.isRange(mw.config.get('wgRelevantUserName')); $form.find('[name=actiontype][value=template]').prop('disabled', originalIsRange).prop('checked', !originalIsRange); // Refetch/reprocess user info then regenerate the main content const regenerateForm = function() { // Tweak titlebar text. In theory, we could save the dialog // at initialization and then use `.setTitle`, but in practice that swallows // the scriptName and requires `.display`ing, which jumps the // window. It's just a line of text, so this is fine. const titleBar = dialog.querySelector('.morebits-dialog-title').firstChild.nextSibling; titleBar.nodeValue = titleBar.nodeValue.replace(priorName, relevantUserName); // Tweak unblock link const unblockLink = dialog.querySelector('.morebits-dialog-footerlinks a'); unblockLink.href = unblockLink.href.replace(priorName, relevantUserName); unblockLink.title = unblockLink.title.replace(priorName, relevantUserName); // Correct partial state $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.getPref('defaultToPartialBlocks')); if (blockedUserName === relevantUserName) { $form.find('[name=actiontype][value=partial]').prop('checked', Twinkle.block.currentBlockInfo.partial === ''); } // Set content appropriately Twinkle.block.callback.change_action(e); }; if (Twinkle.block.fetchedData[relevantUserName]) { Twinkle.block.processUserInfo(Twinkle.block.fetchedData[relevantUserName], regenerateForm); } else { Twinkle.block.fetchUserInfo(regenerateForm); } }; Twinkle.block.callback.change_action = function twinkleblockCallbackChangeAction(e) { let fieldPreset, fieldTemplateOptions, fieldBlockOptions; const $form = $(e.target.form); // Make ifs shorter const blockBox = $form.find('[name=actiontype][value=block]').is(':checked'); const templateBox = $form.find('[name=actiontype][value=template]').is(':checked'); const $partial = $form.find('[name=actiontype][value=partial]'); const partialBox = $partial.is(':checked'); let blockGroup = partialBox ? Twinkle.block.blockGroupsPartial : Twinkle.block.blockGroups; $partial.prop('disabled', !blockBox && !templateBox); // Add current block parameters as default preset const prior = { label: 'Prior block' }; if (blockedUserName === relevantUserName) { Twinkle.block.blockPresetsInfo.prior = Twinkle.block.currentBlockInfo; // value not a valid template selection, chosen below by setting templateName prior.list = [{ label: 'Prior block settings', value: 'prior', selected: true }]; // Arrays of objects are annoying to check if (!blockGroup.some((bg) => bg.label === prior.label)) { blockGroup.push(prior); } // Always ensure proper template exists/is selected when switching modes if (partialBox) { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-pblockindef' : 'uw-pblock'; } else { if (!Twinkle.block.isRegistered) { Twinkle.block.blockPresetsInfo.prior.templateName = 'uw-ablock'; } else { Twinkle.block.blockPresetsInfo.prior.templateName = Morebits.string.isInfinity(Twinkle.block.currentBlockInfo.expiry) ? 'uw-blockindef' : 'uw-block'; } } } else { // But first remove any prior prior blockGroup = blockGroup.filter((bg) => bg.label !== prior.label); } // Can be in preset or template field, so the old one in the template // field will linger. No need to keep the old value around, so just // remove it; saves trouble when hiding/evaluating $form.find('[name=dstopic]').parent().remove(); Twinkle.block.callback.saveFieldset($('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($('[name=field_template_options]')); if (blockBox) { fieldPreset = new Morebits.QuickForm.Element({ type: 'field', label: 'Preset', name: 'field_preset' }); fieldPreset.append({ type: 'select', name: 'preset', label: 'Choose a preset:', event: Twinkle.block.callback.change_preset, list: Twinkle.block.callback.filtered_block_groups(blockGroup) }); fieldBlockOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Block options', name: 'field_block_options' }); fieldBlockOptions.append({ type: 'div', name: 'currentblock', label: ' ' }); fieldBlockOptions.append({ type: 'div', name: 'hasblocklog', label: ' ' }); fieldBlockOptions.append({ type: 'select', name: 'expiry_preset', label: 'Expiry:', event: Twinkle.block.callback.change_expiry, list: [ { label: 'custom', value: 'custom', selected: true }, { label: 'indefinite', value: 'infinity' }, { label: '3 hours', value: '3 hours' }, { label: '12 hours', value: '12 hours' }, { label: '24 hours', value: '24 hours' }, { label: '31 hours', value: '31 hours' }, { label: '36 hours', value: '36 hours' }, { label: '48 hours', value: '48 hours' }, { label: '60 hours', value: '60 hours' }, { label: '72 hours', value: '72 hours' }, { label: '1 week', value: '1 week' }, { label: '2 weeks', value: '2 weeks' }, { label: '1 month', value: '1 month' }, { label: '3 months', value: '3 months' }, { label: '6 months', value: '6 months' }, { label: '1 year', value: '1 year' }, { label: '2 years', value: '2 years' }, { label: '3 years', value: '3 years' } ] }); fieldBlockOptions.append({ type: 'input', name: 'expiry', label: 'Custom expiry', tooltip: 'You can use relative times, like "1 minute" or "19 days", or absolute timestamps, "yyyymmddhhmm" (e.g. "200602011405" is Feb 1, 2006, at 14:05 UTC).', value: Twinkle.block.field_block_options.expiry || Twinkle.block.field_template_options.template_expiry }); if (partialBox) { // Partial block fieldBlockOptions.append({ type: 'select', multiple: true, name: 'pagerestrictions', label: 'Specific pages to block from editing', value: '', tooltip: '10 page max.' }); const ns = fieldBlockOptions.append({ type: 'select', multiple: true, name: 'namespacerestrictions', label: 'Namespace blocks', value: '', tooltip: 'Block from editing these namespaces.' }); $.each(menuFormattedNamespaces, (number, name) => { // Ignore -1: Special; -2: Media; and 2300-2303: Gadget (talk) and Gadget definition (talk) if (number >= 0 && number < 830) { ns.append({ type: 'option', label: name, value: number }); } }); } const blockoptions = [ { checked: Twinkle.block.field_block_options.nocreate, label: 'Block account creation', name: 'nocreate', value: '1' } ]; if (Twinkle.block.isRegistered && !mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { blockoptions.push({ checked: Twinkle.block.field_block_options.noemail, label: 'Block user from sending email', name: 'noemail', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.disabletalk, label: 'Prevent this user from editing their own talk page while blocked', name: 'disabletalk', value: '1', tooltip: partialBox ? 'If issuing a partial block, this MUST remain unchecked unless you are also preventing them from editing User talk space' : '' }); if (Twinkle.block.isRegistered) { blockoptions.push({ checked: Twinkle.block.field_block_options.autoblock, label: 'Autoblock any IP addresses used (hardblock)', name: 'autoblock', value: '1' }); } else { blockoptions.push({ checked: Twinkle.block.field_block_options.hardblock, label: 'Block logged-in users from using this IP address (hardblock)', name: 'hardblock', value: '1' }); } blockoptions.push({ checked: Twinkle.block.field_block_options.watchuser, label: 'Watch user and user talk pages', name: 'watchuser', value: '1' }); fieldBlockOptions.append({ type: 'checkbox', name: 'blockoptions', list: blockoptions }); fieldBlockOptions.append({ type: 'textarea', label: 'Reason (for block log):', name: 'reason', tooltip: 'Consider adding helpful details to the default message.', value: Twinkle.block.field_block_options.reason }); fieldBlockOptions.append({ type: 'div', name: 'filerlog_label', label: 'See also:', style: 'display:inline-block;font-style:normal !important', tooltip: 'Insert a "see also" message to indicate whether the filter log, deleted contributions or related temporary accounts played a role in the decision to block.' }); fieldBlockOptions.append({ type: 'checkbox', name: 'filter_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Filter log', checked: false, value: 'filter log' } ] }); fieldBlockOptions.append({ type: 'checkbox', name: 'deleted_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block; margin-right:5px', list: [ { label: 'Deleted contribs', checked: false, value: 'deleted contribs' } ] }); if (mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName'))) { fieldBlockOptions.append({ type: 'checkbox', name: 'related_see_also', event: Twinkle.block.callback.toggle_see_alsos, style: 'display:inline-block', list: [ { label: 'Related temporary accounts', checked: false, value: 'related temporary accounts' } ] }); } // Yet-another-logevents-doesn't-handle-ranges-well if (blockedUserName === relevantUserName) { fieldBlockOptions.append({ type: 'hidden', name: 'reblock', value: '1' }); } } // grab discretionary sanctions list from en-wiki Twinkle.block.dsinfo = Morebits.wiki.getCachedJson('Template:Ds/topics.json'); Twinkle.block.dsinfo.then((dsinfo) => { const $select = $('[name="dstopic"]'); const $options = $.map(dsinfo, (value, key) => $('<option>').val(value.code).text(key).prop('label', key)); $select.append($options); }); // DS selection visible in either the template field set or preset, // joint settings saved here const dsSelectSettings = { type: 'select', name: 'dstopic', label: 'DS topic', value: '', tooltip: 'If selected, it will inform the template and may be added to the blocking message', event: Twinkle.block.callback.toggle_ds_reason }; if (templateBox) { fieldTemplateOptions = new Morebits.QuickForm.Element({ type: 'field', label: 'Template options', name: 'field_template_options' }); fieldTemplateOptions.append({ type: 'select', name: 'template', label: 'Choose talk page template:', event: Twinkle.block.callback.change_template, list: Twinkle.block.callback.filtered_block_groups(blockGroup, true), value: Twinkle.block.field_template_options.template }); // Only visible for aeblock and aepblock, toggled in change_template fieldTemplateOptions.append(dsSelectSettings); fieldTemplateOptions.append({ type: 'input', name: 'article', label: 'Linked page', value: '', tooltip: 'A page can be linked within the notice, perhaps if it was the primary target of disruption. Leave empty for no page to be linked.' }); // Only visible if partial and not blocking fieldTemplateOptions.append({ type: 'input', name: 'area', label: 'Area blocked from', value: '', tooltip: 'Optional explanation of the pages or namespaces the user was blocked from editing.' }); if (!blockBox) { fieldTemplateOptions.append({ type: 'input', name: 'template_expiry', label: 'Period of blocking:', value: '', tooltip: 'The period the blocking is due for, for example 24 hours, 2 weeks, indefinite etc...' }); } fieldTemplateOptions.append({ type: 'input', name: 'block_reason', label: '"You have been blocked for ..."', tooltip: 'An optional reason, to replace the default generic reason. Only available for the generic block templates.', value: Twinkle.block.field_template_options.block_reason }); if (blockBox) { fieldTemplateOptions.append({ type: 'checkbox', name: 'blank_duration', list: [ { label: 'Do not include expiry in template', checked: Twinkle.block.field_template_options.blank_duration, tooltip: 'Instead of including the duration, make the block template read "You have been blocked temporarily..."' } ] }); } else { fieldTemplateOptions.append({ type: 'checkbox', list: [ { label: 'Talk page access disabled', name: 'notalk', checked: Twinkle.block.field_template_options.notalk, tooltip: 'Make the block template state that the user\'s talk page access has been removed' }, { label: 'User blocked from sending email', name: 'noemail_template', checked: Twinkle.block.field_template_options.noemail_template, tooltip: 'If the area is not provided, make the block template state that the user\'s email access has been removed' }, { label: 'User blocked from creating accounts', name: 'nocreate_template', checked: Twinkle.block.field_template_options.nocreate_template, tooltip: 'If the area is not provided, make the block template state that the user\'s ability to create accounts has been removed' } ] }); } const $previewlink = $('<a id="twinkleblock-preview-link">Preview</a>'); $previewlink.off('click').on('click', () => { Twinkle.block.callback.preview($form[0]); }); $previewlink.css({cursor: 'pointer'}); fieldTemplateOptions.append({ type: 'div', id: 'blockpreview', label: [$previewlink[0]] }); fieldTemplateOptions.append({ type: 'div', id: 'twinkleblock-previewbox', style: 'display: none' }); } else if (fieldPreset) { // Only visible for arbitration enforcement, toggled in change_preset fieldPreset.append(dsSelectSettings); } let oldfield; if (fieldPreset) { oldfield = $form.find('fieldset[name="field_preset"]')[0]; oldfield.parentNode.replaceChild(fieldPreset.render(), oldfield); } else { $form.find('fieldset[name="field_preset"]').hide(); } if (fieldBlockOptions) { oldfield = $form.find('fieldset[name="field_block_options"]')[0]; oldfield.parentNode.replaceChild(fieldBlockOptions.render(), oldfield); $form.find('fieldset[name="field_64"]').show(); $form.find('[name=pagerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', placeholder: 'Select pages to block user from', language: { errorLoading: function() { return 'Incomplete or invalid search term'; } }, maximumSelectionLength: 10, // Software limitation [[phab:T202776]] minimumInputLength: 1, // prevent ajax call when empty ajax: { url: mw.util.wikiScript('api'), dataType: 'json', delay: 100, data: function(params) { const title = mw.Title.newFromText(params.term); if (!title) { return; } return { action: 'query', format: 'json', list: 'allpages', apfrom: title.title, apnamespace: title.namespace, aplimit: '10' }; }, processResults: function(data) { return { results: data.query.allpages.map((page) => { const title = mw.Title.newFromText(page.title, page.ns).toText(); return { id: title, text: title }; }) }; } }, templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl(choice.text), target: '_blank' }); } }); $form.find('[name=namespacerestrictions]').select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matchers.wordBeginning, language: { searching: Morebits.select2.queryInterceptor }, templateResult: Morebits.select2.highlightSearchMatches, placeholder: 'Select namespaces to block user from' }); mw.util.addCSS( // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); } else { $form.find('fieldset[name="field_block_options"]').hide(); $form.find('fieldset[name="field_64"]').hide(); // Clear select2 options $form.find('[name=pagerestrictions]').val(null).trigger('change'); $form.find('[name=namespacerestrictions]').val(null).trigger('change'); } if (fieldTemplateOptions) { oldfield = $form.find('fieldset[name="field_template_options"]')[0]; oldfield.parentNode.replaceChild(fieldTemplateOptions.render(), oldfield); e.target.form.root.previewer = new Morebits.wiki.Preview($(e.target.form.root).find('#twinkleblock-previewbox').last()[0]); } else { $form.find('fieldset[name="field_template_options"]').hide(); } // Any block, including ranges if (Twinkle.block.currentBlockInfo) { // false for an ip covered by a range or a smaller range within a larger range; // true for a user, single ip block, or the exact range for a range block const sameUser = blockedUserName === relevantUserName; Morebits.Status.init($('div[name="currentblock"] span').last()[0]); let statusStr = relevantUserName + ' is ' + (Twinkle.block.currentBlockInfo.partial === '' ? 'partially blocked' : 'blocked sitewide'); // Range blocked if (Twinkle.block.currentBlockInfo.rangestart !== Twinkle.block.currentBlockInfo.rangeend) { if (sameUser) { statusStr += ' as a rangeblock'; } else { statusStr += ' within a' + (Morebits.ip.get64(relevantUserName) === blockedUserName ? ' /64' : '') + ' rangeblock'; // Link to the full range const $rangeblockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: blockedUserName, type: 'block'}) + '">' + blockedUserName + '</a>)')); statusStr += ' (' + $rangeblockloglink.html() + ')'; } } if (Twinkle.block.currentBlockInfo.expiry === 'infinity') { statusStr += ' (indefinite)'; } else if (new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).isValid()) { statusStr += ' (expires ' + new Morebits.Date(Twinkle.block.currentBlockInfo.expiry).calendar('utc') + ')'; } let infoStr = 'This form will'; if (sameUser) { infoStr += ' change that block'; if (Twinkle.block.currentBlockInfo.partial === undefined && partialBox) { infoStr += ', converting it to a partial block'; } else if (Twinkle.block.currentBlockInfo.partial === '' && !partialBox) { infoStr += ', converting it to a sitewide block'; } infoStr += '.'; } else { infoStr += ' add an additional ' + (partialBox ? 'partial ' : '') + 'block.'; } Morebits.Status.warn(statusStr, infoStr); // Default to the current block conditions on intial form generation Twinkle.block.callback.update_form(e, Twinkle.block.currentBlockInfo); } // This is where T146628 really comes into play: a rangeblock will // only return the correct block log if wgRelevantUserName is the // exact range, not merely a funtional equivalent if (Twinkle.block.hasBlockLog) { const $blockloglink = $('<span>').append($('<a target="_blank" href="' + mw.util.getUrl('Special:Log', {action: 'view', page: relevantUserName, type: 'block'}) + '">block log</a>)')); if (!Twinkle.block.currentBlockInfo) { const lastBlockAction = Twinkle.block.blockLog[0]; if (lastBlockAction.action === 'unblock') { $blockloglink.append(' (unblocked ' + new Morebits.Date(lastBlockAction.timestamp).calendar('utc') + ')'); } else { // block or reblock $blockloglink.append(' (' + lastBlockAction.params.duration + ', expired ' + new Morebits.Date(lastBlockAction.params.expiry).calendar('utc') + ')'); } } Morebits.Status.init($('div[name="hasblocklog"] span').last()[0]); Morebits.Status.warn(Twinkle.block.currentBlockInfo ? 'Previous blocks' : 'This ' + (Morebits.ip.isRange(relevantUserName) ? 'range' : 'user') + ' has been blocked in the past', $blockloglink[0]); } // Make sure all the fields are correct based on initial defaults if (blockBox) { Twinkle.block.callback.change_preset(e); } else if (templateBox) { Twinkle.block.callback.change_template(e); } }; /* * Keep alphabetized by key name, Twinkle.block.blockGroups establishes * the order they will appear in the interface * * Block preset format, all keys accept only 'true' (omit for false) except where noted: * <title of block template> : { * autoblock: <autoblock any IP addresses used (for registered users only)> * disabletalk: <disable user from editing their own talk page while blocked> * expiry: <string - expiry timestamp, can include relative times like "5 months", "2 weeks" etc> * forIPsOnly: <show block option in the interface only if the relevant user is an IP> * forTempAccountsOnly: <show block option in the interface only if the relevant user is a temporary account> * forRegisteredOnly: <show block option in the interface only if the relevant user is a temporary account or regular account> * label: <string - label for the option of the dropdown in the interface (keep brief)> * noemail: prevent the user from sending email through Special:Emailuser * pageParam: <set if the associated block template accepts a page parameter> * prependReason: <string - prepends the value of 'reason' to the end of the existing reason, namely for when revoking talk page access> * nocreate: <block account creation from the user's IP (for unregistered users only)> * nonstandard: <template does not conform to stewardship of WikiProject User Warnings and may not accept standard parameters> * reason: <string - block rationale, as would appear in the block log, * and the edit summary for when adding block template, unless 'summary' is set> * reasonParam: <set if the associated block template accepts a reason parameter> * sig: <string - set to ~~~~ if block template does not accept "true" as the value, or set null to omit sig param altogether> * summary: <string - edit summary for when adding block template to user's talk page, if not set, 'reason' is used> * suppressArticleInSummary: <set to suppress showing the article name in the edit summary, as with attack pages> * templateName: <string - name of template to use (instead of key name), entry will be omitted from the Templates list. * (e.g. use another template but with different block options)> * useInitialOptions: <when preset is chosen, only change given block options, leave others as they were> * * WARNING: 'anononly' and 'allowusertalk' are enabled by default. * To disable, set 'hardblock' and 'disabletalk', respectively */ Twinkle.block.blockPresetsInfo = { anonblock: { expiry: '31 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}}', sig: '~~~~' }, 'anonblock - school': { expiry: '36 hours', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{anonblock}} <!-- Likely a school based on behavioral evidence -->', templateName: 'anonblock', sig: '~~~~' }, 'blocked proxy': { expiry: '1 year', forIPsOnly: true, nocreate: true, nonstandard: true, hardblock: true, reason: '{{blocked proxy}}', sig: null }, 'CheckUser block': { expiry: '1 week', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{CheckUser block}}', sig: '~~~~' }, 'checkuserblock-account': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-account}}', sig: '~~~~' }, 'checkuserblock-wide': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{checkuserblock-wide}}', sig: '~~~~' }, colocationwebhost: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{colocationwebhost}}', sig: null }, oversightblock: { autoblock: true, expiry: 'infinity', nocreate: true, nonstandard: true, reason: '{{OversightBlock}}', sig: '~~~~' }, 'school block': { forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{school block}}', sig: '~~~~' }, spamblacklistblock: { forIPsOnly: true, expiry: '1 month', disabletalk: true, nocreate: true, reason: '{{spamblacklistblock}} <!-- editor only attempts to add blacklisted links, see [[Special:Log/spamblacklist]] -->' }, rangeblock: { reason: '{{rangeblock}}', nocreate: true, nonstandard: true, forIPsOnly: true, sig: '~~~~' }, tor: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{Tor}}', sig: null }, webhostblock: { expiry: '1 year', forIPsOnly: true, nonstandard: true, reason: '{{webhostblock}}', sig: null }, // uw-prefixed 'uw-3block': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: 'Violation of the [[WP:Three-revert rule|three-revert rule]]', summary: 'You have been blocked from editing for violation of the [[WP:3RR|three-revert rule]]' }, 'uw-ablock': { autoblock: true, expiry: '31 hours', forIPsOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'Your IP address has been blocked from editing', suppressArticleInSummary: true }, 'uw-adblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes', summary: 'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]' }, 'uw-aeblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been blocked from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-bioblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Violations of the [[WP:Biographies of living persons|biographies of living persons]] policy', summary: 'You have been blocked from editing for violations of Wikipedia\'s [[WP:BLP|biographies of living persons policy]]' }, 'uw-block': { autoblock: true, expiry: '24 hours', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing', suppressArticleInSummary: true }, 'uw-blockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been indefinitely blocked from editing', suppressArticleInSummary: true }, 'uw-blocknotalk': { autoblock: true, disabletalk: true, nocreate: true, pageParam: true, reasonParam: true, summary: 'You have been blocked from editing and your user talk page access has been disabled', suppressArticleInSummary: true }, 'uw-botblock': { forRegisteredOnly: true, pageParam: true, reason: 'Running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]', summary: 'You have been blocked from editing because it appears you are running a [[WP:BOT|bot script]] without [[WP:BRFA|approval]]' }, 'uw-botublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-botublock}} <!-- Username implies a bot, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] indicates this is a [[WP:BOT|bot]] account, which is currently not approved' }, 'uw-botuhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-botuhblock}} <!-- Username implies a bot, hard block -->', summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-causeblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-causeblock}} <!-- Username represents a non-profit, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-compblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: 'Compromised account', summary: 'You have been indefinitely blocked from editing because it is believed that your [[WP:SECURE|account has been compromised]]' }, 'uw-copyrightblock': { autoblock: true, expiry: 'infinity', nocreate: true, pageParam: true, reason: '[[WP:Copyright violations|Copyright violations]]', summary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]' }, 'uw-dblock': { autoblock: true, nocreate: true, reason: 'Persistent removal of content', pageParam: true, summary: 'You have been blocked from editing for continued [[WP:VAND|removal of material]]' }, 'uw-disruptblock': { autoblock: true, nocreate: true, reason: '[[WP:Disruptive editing|Disruptive editing]]', summary: 'You have been blocked from editing for [[WP:DE|disruptive editing]]' }, 'uw-efblock': { autoblock: true, nocreate: true, reason: 'Repeatedly triggering the [[WP:Edit filter|Edit filter]]', summary: 'You have been blocked from editing for disruptive edits that repeatedly triggered the [[WP:EF|edit filter]]' }, 'uw-ewblock': { autoblock: true, expiry: '24 hours', nocreate: true, pageParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been blocked from editing to prevent further [[WP:DE|disruption]] caused by your engagement in an [[WP:EW|edit war]]' }, 'uw-hblock': { autoblock: true, nocreate: true, pageParam: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for attempting to [[WP:HARASS|harass]] other users' }, 'uw-ipevadeblock': { forIPsOnly: true, expiry: '1 week', nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your IP address has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-lblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Making [[WP:No legal threats|legal threats]]', summary: 'You have been blocked from editing for making [[WP:NLT|legal threats or taking legal action]]' }, 'uw-nothereblock': { autoblock: true, expiry: 'infinity', nocreate: true, reason: 'Clearly [[WP:NOTHERE|not here to build an encyclopedia]]', forRegisteredOnly: true, summary: 'You have been indefinitely blocked from editing because it appears that you are not here to [[WP:NOTHERE|build an encyclopedia]]' }, 'uw-npblock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages', summary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]' }, 'uw-pablock': { autoblock: true, expiry: '31 hours', nocreate: true, reason: '[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]', summary: 'You have been blocked from editing for making [[WP:NPA|personal attacks]] toward other users' }, 'uw-sblock': { autoblock: true, nocreate: true, reason: 'Using Wikipedia for [[WP:SPAM|spam]] purposes', summary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes' }, 'uw-soablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam, advertising, or promotion]]' }, 'uw-socialmediablock': { autoblock: true, nocreate: true, pageParam: true, reason: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]', summary: 'You have been blocked from editing for using user and/or article pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]' }, 'uw-sockblock': { autoblock: true, forRegisteredOnly: true, nocreate: true, reason: 'Abusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been blocked from editing for abusing [[WP:SOCK|multiple accounts]]' }, 'uw-softerblock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-softerblock}} <!-- Promotional username, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] gives the impression that the account represents a group, organization or website' }, 'uw-spamublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-spamublock}} <!-- Promotional username, promotional edits -->', summary: 'You have been indefinitely blocked from editing because your account is being used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the [[WP:U|username policy]]' }, 'uw-spoablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '[[WP:SOCK|Sock puppetry]]', summary: 'This account has been blocked as a [[WP:SOCK|sock puppet]] created to violate Wikipedia policy' }, 'uw-talkrevoked': { disabletalk: true, reason: 'Revoking talk page access: inappropriate use of user talk page while blocked', prependReason: true, summary: 'Your user talk page access has been disabled', useInitialOptions: true }, 'uw-tempevadeblock': { autoblock: true, expiry: 'infinity', forTempAccountsOnly: true, nocreate: true, reason: '[[WP:Blocking policy#Evasion of blocks|Block evasion]]', summary: 'Your temporary account has been blocked from editing because it has been used to [[WP:EVADE|evade a previous block]]' }, 'uw-ublock': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock}} <!-- Username violation, soft block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a violation of the [[WP:U|username policy]]' }, 'uw-ublock-double': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-double}} <!-- Username closely resembles another user, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] is too similar to the username of another Wikipedia user' }, 'uw-ucblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: 'Persistent addition of [[WP:INTREF|unsourced content]]', summary: 'You have been blocked from editing for persistent addition of [[WP:INTREF|unsourced content]]' }, 'uw-uhblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock}} <!-- Username violation, hard block -->', reasonParam: true, summary: 'You have been indefinitely blocked from editing because your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-ublock-wellknown': { expiry: 'infinity', forRegisteredOnly: true, reason: '{{uw-ublock-wellknown}} <!-- Username represents a well-known person, soft block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] matches the name of a well-known living individual' }, 'uw-uhblock-double': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, reason: '{{uw-uhblock-double}} <!-- Attempted impersonation of another user, hard block -->', summary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] appears to impersonate another established Wikipedia user' }, 'uw-upeblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:PAID|Undisclosed paid editing]] in violation of the WMF [[WP:TOU|Terms of Use]]', summary: 'You have been indefinitely blocked from editing because your account is being used in violation of [[WP:PAID|Wikipedia policy on undisclosed paid advocacy]]' }, 'uw-vaublock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '{{uw-vaublock}} <!-- Username violation, vandalism-only account -->', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]] and your username is a blatant violation of the [[WP:U|username policy]]' }, 'uw-vblock': { autoblock: true, expiry: '31 hours', nocreate: true, pageParam: true, reason: '[[WP:Vandalism|Vandalism]]', summary: 'You have been blocked from editing to prevent further [[WP:VAND|vandalism]]' }, 'uw-voablock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: true, reason: '[[WP:DISRUPTONLY|Vandalism-only account]]', summary: 'You have been indefinitely blocked from editing because your account is being [[WP:DISRUPTONLY|used only for vandalism]]' }, 'zombie proxy': { expiry: '1 month', forIPsOnly: true, nocreate: true, nonstandard: true, reason: '{{zombie proxy}}', sig: null }, // Begin partial block templates, accessed in Twinkle.block.blockGroupsPartial 'uw-acpblock': { autoblock: true, expiry: '48 hours', nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-acpblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: true, pageParam: false, reasonParam: true, reason: 'Misusing [[WP:Sock puppetry|multiple accounts]]', summary: 'You have been indefinitely [[WP:PB|blocked from creating accounts]] for misusing [[WP:SOCK|multiple accounts]]' }, 'uw-aepblock': { autoblock: true, nocreate: false, pageParam: false, reason: '[[WP:Arbitration enforcement|Arbitration enforcement]]', reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from editing for violating an [[WP:Arbitration|arbitration decision]]' }, 'uw-epblock': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, noemail: true, pageParam: false, reasonParam: true, reason: 'Email [[WP:Harassment|harassment]]', summary: 'You have been [[WP:PB|blocked from emailing]] other editors for [[WP:Harassment|harassment]]' }, 'uw-ewpblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, reason: '[[WP:Edit warring|Edit warring]]', summary: 'You have been [[WP:PB|partially blocked]] from editing certain areas of the encyclopedia to prevent further [[WP:DE|disruption]] due to [[WP:EW|edit warring]]' }, 'uw-pblock': { autoblock: true, expiry: '24 hours', nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been [[WP:PB|partially blocked]] from certain areas of the encyclopedia' }, 'uw-pblockindef': { autoblock: true, expiry: 'infinity', forRegisteredOnly: true, nocreate: false, pageParam: false, reasonParam: true, summary: 'You have been indefinitely [[WP:PB|partially blocked]] from certain areas of the encyclopedia' } }; Twinkle.block.transformBlockPresets = function twinkleblockTransformBlockPresets() { // supply sensible defaults $.each(Twinkle.block.blockPresetsInfo, (preset, settings) => { settings.summary = settings.summary || settings.reason; settings.sig = settings.sig !== undefined ? settings.sig : 'yes'; settings.indefinite = settings.indefinite || Morebits.string.isInfinity(settings.expiry); if (!Twinkle.block.isRegistered && settings.indefinite) { settings.expiry = '31 hours'; } else { settings.expiry = settings.expiry || '31 hours'; } Twinkle.block.blockPresetsInfo[preset] = settings; }); }; // These are the groups of presets and defines the order in which they appear. For each list item: // label: <string, the description that will be visible in the dropdown> // value: <string, the key of a preset in blockPresetsInfo> Twinkle.block.blockGroups = [ { label: 'Common block reasons', list: [ { label: 'Anonblock', value: 'anonblock' }, { label: 'Anonblock – likely a school', value: 'anonblock - school' }, { label: 'School block', value: 'school block' }, { label: 'Generic block (custom reason)', value: 'uw-block' }, // ends up being default for registered users { label: 'Generic block (custom reason) – IP', value: 'uw-ablock', selected: true }, // set only when blocking IP { label: 'Generic block (custom reason) – indefinite', value: 'uw-blockindef' }, { label: 'Disruptive editing', value: 'uw-disruptblock' }, { label: 'Inappropriate use of user talk page while blocked', value: 'uw-talkrevoked' }, { label: 'Not here to build an encyclopedia', value: 'uw-nothereblock' }, { label: 'Unsourced content', value: 'uw-ucblock' }, { label: 'Vandalism', value: 'uw-vblock' }, { label: 'Vandalism-only account', value: 'uw-voablock' } ] }, { label: 'Extended reasons', list: [ { label: 'Advertising', value: 'uw-adblock' }, { label: 'Arbitration enforcement', value: 'uw-aeblock' }, { label: 'Block evasion – IP', value: 'uw-ipevadeblock' }, { label: 'Block evasion – temporary account', value: 'uw-tempevadeblock' }, { label: 'BLP violations', value: 'uw-bioblock' }, { label: 'Copyright violations', value: 'uw-copyrightblock' }, { label: 'Creating nonsense pages', value: 'uw-npblock' }, { label: 'Edit filter-related', value: 'uw-efblock' }, { label: 'Edit warring', value: 'uw-ewblock' }, { label: 'Generic block with talk page access revoked', value: 'uw-blocknotalk' }, { label: 'Harassment', value: 'uw-hblock' }, { label: 'Legal threats', value: 'uw-lblock' }, { label: 'Personal attacks or harassment', value: 'uw-pablock' }, { label: 'Possible compromised account', value: 'uw-compblock' }, { label: 'Removal of content', value: 'uw-dblock' }, { label: 'Sock puppetry (master)', value: 'uw-sockblock' }, { label: 'Sock puppetry (puppet)', value: 'uw-spoablock' }, { label: 'Social networking', value: 'uw-socialmediablock' }, { label: 'Spam', value: 'uw-sblock' }, { label: 'Spam/advertising-only account', value: 'uw-soablock' }, { label: 'Unapproved bot', value: 'uw-botblock' }, { label: 'Undisclosed paid editing', value: 'uw-upeblock' }, { label: 'Violating the three-revert rule', value: 'uw-3block' } ] }, { label: 'Username violations', list: [ { label: 'Bot username, soft block', value: 'uw-botublock' }, { label: 'Bot username, hard block', value: 'uw-botuhblock' }, { label: 'Promotional username, hard block', value: 'uw-spamublock' }, { label: 'Promotional username, soft block', value: 'uw-softerblock' }, { label: 'Similar username, soft block', value: 'uw-ublock-double' }, { label: 'Username violation, soft block', value: 'uw-ublock' }, { label: 'Username violation, hard block', value: 'uw-uhblock' }, { label: 'Username impersonation, hard block', value: 'uw-uhblock-double' }, { label: 'Username represents a well-known person, soft block', value: 'uw-ublock-wellknown' }, { label: 'Username represents a non-profit, soft block', value: 'uw-causeblock' }, { label: 'Username violation, vandalism-only account', value: 'uw-vaublock' } ] }, { label: 'Templated reasons', list: [ { label: 'Blocked proxy', value: 'blocked proxy' }, { label: 'CheckUser block', value: 'CheckUser block' }, { label: 'CheckUser block – account', value: 'checkuserblock-account' }, { label: 'CheckUser block – wide', value: 'checkuserblock-wide' }, { label: 'Colocation webhost', value: 'colocationwebhost' }, { label: 'Oversight block', value: 'oversightblock' }, { label: 'Rangeblock', value: 'rangeblock' }, // Only for IP ranges, selected for non-/64 ranges in filtered_block_groups { label: 'Spam blacklist block', value: 'spamblacklistblock' }, { label: 'Tor', value: 'tor' }, { label: 'Webhost block', value: 'webhostblock' }, { label: 'Zombie proxy', value: 'zombie proxy' } ] } ]; Twinkle.block.blockGroupsPartial = [ { label: 'Common partial block reasons', list: [ { label: 'Generic partial block (custom reason)', value: 'uw-pblock', selected: true }, { label: 'Generic partial block (custom reason) – indefinite', value: 'uw-pblockindef' }, { label: 'Edit warring', value: 'uw-ewpblock' } ] }, { label: 'Extended partial block reasons', list: [ { label: 'Arbitration enforcement', value: 'uw-aepblock' }, { label: 'Email harassment', value: 'uw-epblock' }, { label: 'Misusing multiple accounts', value: 'uw-acpblock' }, { label: 'Misusing multiple accounts – indefinite', value: 'uw-acpblockindef' } ] } ]; Twinkle.block.callback.filtered_block_groups = function twinkleblockCallbackFilteredBlockGroups(group, showTemplate) { return $.map(group, (blockGroup) => { const list = $.map(blockGroup.list, (blockPreset) => { switch (blockPreset.value) { case 'uw-talkrevoked': if (blockedUserName !== relevantUserName) { return; } break; case 'rangeblock': if (!Morebits.ip.isRange(relevantUserName)) { return; } blockPreset.selected = !Morebits.ip.get64(relevantUserName); break; case 'CheckUser block': case 'checkuserblock-account': case 'checkuserblock-wide': if (!Morebits.userIsInGroup('checkuser')) { return; } break; case 'oversightblock': if (!Morebits.userIsInGroup('suppress')) { return; } break; default: break; } const blockSettings = Twinkle.block.blockPresetsInfo[blockPreset.value]; let allowedUserType; // for regular users and temporary accounts if (blockSettings.forRegisteredOnly) { allowedUserType = Twinkle.block.isRegistered; // for temporary accounts } else if (blockSettings.forTempAccountsOnly) { allowedUserType = mw.util.isTemporaryUser(mw.config.get('wgRelevantUserName')); // for IPs } else if (blockSettings.forIPsOnly) { allowedUserType = !Twinkle.block.isRegistered; } else { allowedUserType = true; } if (!(blockSettings.templateName && showTemplate) && allowedUserType) { const templateName = blockSettings.templateName || blockPreset.value; return { label: (showTemplate ? '{{' + templateName + '}}: ' : '') + blockPreset.label, value: blockPreset.value, data: [{ name: 'template-name', value: templateName }], selected: !!blockPreset.selected, disabled: !!blockPreset.disabled }; } }); if (list.length) { return { label: blockGroup.label, list: list }; } }); }; Twinkle.block.callback.change_preset = function twinkleblockCallbackChangePreset(e) { const form = e.target.form, key = form.preset.value; if (!key) { return; } Twinkle.block.callback.update_form(e, Twinkle.block.blockPresetsInfo[key]); if (form.template) { form.template.value = Twinkle.block.blockPresetsInfo[key].templateName || key; Twinkle.block.callback.change_template(e); } else { Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, key === 'uw-aeblock' || key === 'uw-aepblock'); } }; Twinkle.block.callback.change_expiry = function twinkleblockCallbackChangeExpiry(e) { const expiry = e.target.form.expiry; if (e.target.value === 'custom') { Morebits.QuickForm.setElementVisibility(expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(expiry.parentNode, false); expiry.value = e.target.value; } }; Twinkle.block.seeAlsos = []; Twinkle.block.callback.toggle_see_alsos = function twinkleblockCallbackToggleSeeAlso() { const joinEnum = function(e) { if (e.length >= 3) { return e.slice(0, -1).join(', ') + ' and ' + e[e.length - 1]; } else { return e.join(' and '); } }; const reason = this.form.reason.value.replace( new RegExp('( <!--|;) see also ' + joinEnum(Twinkle.block.seeAlsos) + '( -->)?'), '' ); Twinkle.block.seeAlsos = Twinkle.block.seeAlsos.filter((el) => el !== this.value); if (this.checked) { Twinkle.block.seeAlsos.push(this.value); } const seeAlsoMessage = joinEnum(Twinkle.block.seeAlsos); if (!Twinkle.block.seeAlsos.length) { this.form.reason.value = reason; } else if (reason.includes('{{')) { this.form.reason.value = reason + ' <!-- see also ' + seeAlsoMessage + ' -->'; } else { this.form.reason.value = reason + '; see also ' + seeAlsoMessage; } }; Twinkle.block.dsReason = ''; Twinkle.block.callback.toggle_ds_reason = function twinkleblockCallbackToggleDSReason() { const reason = this.form.reason.value.replace( new RegExp(' ?\\(\\[\\[' + Twinkle.block.dsReason + '\\]\\]\\)'), '' ); Twinkle.block.dsinfo.then((dsinfo) => { const sanctionCode = this.selectedIndex; const sanctionName = this.options[sanctionCode].label; Twinkle.block.dsReason = dsinfo[sanctionName].page; if (!this.value) { this.form.reason.value = reason; } else { this.form.reason.value = reason + ' ([[' + Twinkle.block.dsReason + ']])'; } }); }; Twinkle.block.callback.update_form = function twinkleblockCallbackUpdateForm(e, data) { const form = e.target.form; let expiry = data.expiry; // don't override original expiry if useInitialOptions is set if (!data.useInitialOptions) { if (Date.parse(expiry)) { expiry = new Date(expiry).toGMTString(); form.expiry_preset.value = 'custom'; } else { form.expiry_preset.value = data.expiry || 'custom'; } form.expiry.value = expiry; if (form.expiry_preset.value === 'custom') { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, true); } else { Morebits.QuickForm.setElementVisibility(form.expiry.parentNode, false); } } // boolean-flipped options, more at [[mw:API:Block]] data.disabletalk = data.disabletalk !== undefined ? data.disabletalk : false; data.hardblock = data.hardblock !== undefined ? data.hardblock : false; // disable autoblock if blocking a bot if (Twinkle.block.userIsBot || /bot\b/i.test(relevantUserName)) { data.autoblock = false; } $(form).find('[name=field_block_options]').find(':checkbox').each((i, el) => { // don't override original options if useInitialOptions is set if (data.useInitialOptions && data[el.name] === undefined) { return; } const check = data[el.name] === '' || !!data[el.name]; $(el).prop('checked', check); }); if (data.prependReason && data.reason) { form.reason.value = data.reason + '; ' + form.reason.value; } else { form.reason.value = data.reason || ''; } // Clear and/or set any partial page or namespace restrictions if (form.pagerestrictions) { const $pageSelect = $(form).find('[name=pagerestrictions]'); const $namespaceSelect = $(form).find('[name=namespacerestrictions]'); // Respect useInitialOptions by clearing data when switching presets // In practice, this will always clear, since no partial presets use it if (!data.useInitialOptions) { $pageSelect.val(null).trigger('change'); $namespaceSelect.val(null).trigger('change'); } // Add any preset options; in practice, just used for prior block settings if (data.restrictions) { if (data.restrictions.pages && !$pageSelect.val().length) { const pages = data.restrictions.pages.map((pr) => pr.title); // since page restrictions use an ajax source, we // short-circuit that and just add a new option pages.forEach((page) => { if (!$pageSelect.find("option[value='" + $.escapeSelector(page) + "']").length) { const newOption = new Option(page, page, true, true); $pageSelect.append(newOption); } }); $pageSelect.val($pageSelect.val().concat(pages)).trigger('change'); } if (data.restrictions.namespaces) { $namespaceSelect.val($namespaceSelect.val().concat(data.restrictions.namespaces)).trigger('change'); } } } }; Twinkle.block.callback.change_template = function twinkleblockcallbackChangeTemplate(e) { const form = e.target.form, value = form.template.value, settings = Twinkle.block.blockPresetsInfo[value]; const blockBox = $(form).find('[name=actiontype][value=block]').is(':checked'); const partialBox = $(form).find('[name=actiontype][value=partial]').is(':checked'); const templateBox = $(form).find('[name=actiontype][value=template]').is(':checked'); // Block form is not present if (!blockBox) { if (settings.indefinite || settings.nonstandard) { if (Twinkle.block.prev_template_expiry === null) { Twinkle.block.prev_template_expiry = form.template_expiry.value || ''; } form.template_expiry.parentNode.style.display = 'none'; form.template_expiry.value = 'infinity'; } else if (form.template_expiry.parentNode.style.display === 'none') { if (Twinkle.block.prev_template_expiry !== null) { form.template_expiry.value = Twinkle.block.prev_template_expiry; Twinkle.block.prev_template_expiry = null; } form.template_expiry.parentNode.style.display = 'block'; } if (Twinkle.block.prev_template_expiry) { form.expiry.value = Twinkle.block.prev_template_expiry; } Morebits.QuickForm.setElementVisibility(form.notalk.parentNode, !settings.nonstandard); // Partial Morebits.QuickForm.setElementVisibility(form.noemail_template.parentNode, partialBox); Morebits.QuickForm.setElementVisibility(form.nocreate_template.parentNode, partialBox); } else if (templateBox) { // Only present if block && template forms both visible Morebits.QuickForm.setElementVisibility( form.blank_duration.parentNode, !settings.indefinite && !settings.nonstandard ); } Morebits.QuickForm.setElementVisibility(form.dstopic.parentNode, value === 'uw-aeblock' || value === 'uw-aepblock'); // Only particularly relevant if template form is present Morebits.QuickForm.setElementVisibility(form.article.parentNode, settings && !!settings.pageParam); Morebits.QuickForm.setElementVisibility(form.block_reason.parentNode, settings && !!settings.reasonParam); // Partial block Morebits.QuickForm.setElementVisibility(form.area.parentNode, partialBox && !blockBox); form.root.previewer.closePreview(); }; Twinkle.block.prev_template_expiry = null; Twinkle.block.callback.preview = function twinkleblockcallbackPreview(form) { const params = { article: form.article.value, blank_duration: form.blank_duration ? form.blank_duration.checked : false, disabletalk: form.disabletalk.checked || (form.notalk ? form.notalk.checked : false), expiry: form.template_expiry ? form.template_expiry.value : form.expiry.value, hardblock: Twinkle.block.isRegistered ? form.autoblock.checked : form.hardblock.checked, indefinite: Morebits.string.isInfinity(form.template_expiry ? form.template_expiry.value : form.expiry.value), reason: form.block_reason.value, template: form.template.value, dstopic: form.dstopic.value, partial: $(form).find('[name=actiontype][value=partial]').is(':checked'), pagerestrictions: $(form.pagerestrictions).val() || [], namespacerestrictions: $(form.namespacerestrictions).val() || [], noemail: form.noemail.checked || (form.noemail_template ? form.noemail_template.checked : false), nocreate: form.nocreate.checked || (form.nocreate_template ? form.nocreate_template.checked : false), area: form.area.value }; const templateText = Twinkle.block.callback.getBlockNoticeWikitext(params); form.previewer.beginRender(templateText, 'User_talk:' + relevantUserName); // Force wikitext/correct username }; Twinkle.block.callback.evaluate = function twinkleblockCallbackEvaluate(e) { const $form = $(e.target), toBlock = $form.find('[name=actiontype][value=block]').is(':checked'), toWarn = $form.find('[name=actiontype][value=template]').is(':checked'), toPartial = $form.find('[name=actiontype][value=partial]').is(':checked'); let blockoptions = {}, templateoptions = {}; Twinkle.block.callback.saveFieldset($form.find('[name=field_block_options]')); Twinkle.block.callback.saveFieldset($form.find('[name=field_template_options]')); blockoptions = Twinkle.block.field_block_options; templateoptions = Twinkle.block.field_template_options; templateoptions.disabletalk = !!(templateoptions.disabletalk || blockoptions.disabletalk); templateoptions.hardblock = !!blockoptions.hardblock; delete blockoptions.expiry_preset; // remove extraneous // Partial API requires this to be gone, not false or 0 if (toPartial) { blockoptions.partial = templateoptions.partial = true; } templateoptions.pagerestrictions = $form.find('[name=pagerestrictions]').val() || []; templateoptions.namespacerestrictions = $form.find('[name=namespacerestrictions]').val() || []; // Format for API here rather than in saveFieldset blockoptions.pagerestrictions = templateoptions.pagerestrictions.join('|'); blockoptions.namespacerestrictions = templateoptions.namespacerestrictions.join('|'); // use block settings as warn options where not supplied templateoptions.summary = templateoptions.summary || blockoptions.reason; templateoptions.expiry = templateoptions.template_expiry || blockoptions.expiry; if (toBlock) { if (blockoptions.partial) { if (blockoptions.disabletalk && !blockoptions.namespacerestrictions.includes('3')) { return alert('Partial blocks cannot prevent talk page access unless also restricting them from editing User talk space!'); } if (!blockoptions.namespacerestrictions && !blockoptions.pagerestrictions) { if (!blockoptions.noemail && !blockoptions.nocreate) { // Blank entries technically allowed [[phab:T208645]] return alert('No pages or namespaces were selected, nor were email or account creation restrictions applied; please select at least one option to apply a partial block!'); } else if ((templateoptions.template !== 'uw-epblock' || $form.find('select[name="preset"]').val() !== 'uw-epblock') && // Don't require confirmation if email harassment defaults are set !confirm('You are about to block with no restrictions on page or namespace editing, are you sure you want to proceed?')) { return; } } } if (!blockoptions.expiry) { return alert('Please provide an expiry!'); } else if (Morebits.string.isInfinity(blockoptions.expiry) && !Twinkle.block.isRegistered) { return alert("Can't indefinitely block an IP address!"); } if (!blockoptions.reason) { return alert('Please provide a reason for the block!'); } Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); const statusElement = new Morebits.Status('Executing block'); blockoptions.action = 'block'; blockoptions.user = relevantUserName; // boolean-flipped options blockoptions.anononly = blockoptions.hardblock ? undefined : true; blockoptions.allowusertalk = blockoptions.disabletalk ? undefined : true; /* Check if block status changed while processing the form. There's a lot to consider here. list=blocks provides the current block status, but there are at least two issues with relying on it. First, the id doesn't update on a reblock, meaning the individual parameters need to be compared. This can be done roughly with JSON.stringify - we can thankfully rely on order from the server, although sorting would be fine if not - but falsey values are problematic and is non-ideal. More importantly, list=blocks won't indicate if a non-blocked user is blocked then unblocked. This should be exceedingy rare, but regardless, we thus need to check list=logevents, which has a nicely updating logid parameter. We can't rely just on that, though, since it doesn't account for blocks that have expired on their own. As such, we use both. Using some ternaries, the logid variables are false if there's no logevents, so if they aren't equal we defintely have a changed entry (send confirmation). If they are equal, then either the user was never blocked (the block statuses will be equal, no confirmation) or there's no new block, in which case either a block expired (different statuses, confirmation) or the same block is still active (same status, no confirmation). */ const query = { format: 'json', action: 'query', list: 'blocks|logevents', letype: 'block', lelimit: 1, letitle: 'User:' + blockoptions.user }; // bkusers doesn't catch single IPs blocked as part of a range block if (mw.util.isIPAddress(blockoptions.user, true)) { query.bkip = blockoptions.user; } else { query.bkusers = blockoptions.user; } api.get(query).then((data) => { let block = data.query.blocks[0]; // As with the initial data fetch, if an IP is blocked // *and* rangeblocked, this would only grab whichever // block is more recent, which would likely mean a // mismatch. However, if the rangeblock is updated // while filling out the form, this won't detect that, // but that's probably fine. if (data.query.blocks.length > 1 && block.user !== relevantUserName) { block = data.query.blocks[1]; } const logevents = data.query.logevents[0]; const logid = data.query.logevents.length ? logevents.logid : false; if (logid !== Twinkle.block.blockLogId || !!block !== !!Twinkle.block.currentBlockInfo) { let message = 'The block status of ' + blockoptions.user + ' has changed. '; if (block) { message += 'New status: '; } else { message += 'Last entry: '; } let logExpiry = ''; if (logevents.params.duration) { if (logevents.params.duration === 'infinity') { logExpiry = 'indefinitely'; } else { const expiryDate = new Morebits.Date(logevents.params.expiry); logExpiry += (expiryDate.isBefore(new Date()) ? ', expired ' : ' until ') + expiryDate.calendar(); } } else { // no duration, action=unblock, just show timestamp logExpiry = ' ' + new Morebits.Date(logevents.timestamp).calendar(); } message += Morebits.string.toUpperCaseFirstChar(logevents.action) + 'ed by ' + logevents.user + logExpiry + ' for "' + logevents.comment + '". Do you want to override with your settings?'; if (!confirm(message)) { Morebits.Status.info('Executing block', 'Canceled by user'); return; } blockoptions.reblock = 1; // Writing over a block will fail otherwise } // execute block blockoptions.tags = Twinkle.changeTags; blockoptions.token = mw.user.tokens.get('csrfToken'); const mbApi = new Morebits.wiki.Api('Executing block', blockoptions, (() => { statusElement.info('Completed'); if (toWarn) { Twinkle.block.callback.issue_template(templateoptions); } })); mbApi.post(); }); } else if (toWarn) { Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(e.target); Twinkle.block.callback.issue_template(templateoptions); } else { return alert('Please give Twinkle something to do!'); } }; Twinkle.block.callback.issue_template = function twinkleblockCallbackIssueTemplate(formData) { // Use wgRelevantUserName to ensure the block template goes to a single IP and not to the // "talk page" of an IP range (which does not exist) const userTalkPage = 'User_talk:' + mw.config.get('wgRelevantUserName'); const params = Twinkle.block.combineFormDataAndFieldTemplateOptions( formData, Twinkle.block.blockPresetsInfo[formData.template], Twinkle.block.field_template_options.block_reason, Twinkle.block.field_template_options.notalk, Twinkle.block.field_template_options.noemail_template, Twinkle.block.field_template_options.nocreate_template ); Morebits.wiki.actionCompleted.redirect = userTalkPage; Morebits.wiki.actionCompleted.notice = 'Actions complete, loading user talk page in a few seconds'; const wikipediaPage = new Morebits.wiki.Page(userTalkPage, 'User talk page modification'); wikipediaPage.setCallbackParameters(params); wikipediaPage.load(Twinkle.block.callback.main); }; Twinkle.block.combineFormDataAndFieldTemplateOptions = function(formData, messageData, reason, disabletalk, noemail, nocreate) { return $.extend(formData, { messageData: messageData, reason: reason, disabletalk: disabletalk, noemail: noemail, nocreate: nocreate }); }; Twinkle.block.callback.getBlockNoticeWikitext = function(params) { let text = '{{'; const settings = Twinkle.block.blockPresetsInfo[params.template]; if (!settings.nonstandard) { text += 'subst:' + params.template; if (params.article && settings.pageParam) { text += '|page=' + params.article; } if (params.dstopic) { text += '|topic=' + params.dstopic; } if (!/te?mp|^\s*$|min/.exec(params.expiry)) { if (params.indefinite) { text += '|indef=yes'; } else if (!params.blank_duration && !new Morebits.Date(params.expiry).isValid()) { // Block template wants a duration, not date text += '|time=' + params.expiry; } } if (!Twinkle.block.isRegistered && !params.hardblock) { text += '|anon=yes'; } if (params.reason) { text += '|reason=' + params.reason; } if (params.disabletalk) { text += '|notalk=yes'; } // Currently, all partial block templates are "standard" // Building the template, however, takes a fair bit of logic if (params.partial) { if (params.pagerestrictions.length || params.namespacerestrictions.length) { const makeSentence = function(array) { if (array.length < 3) { return array.join(' and '); } const last = array.pop(); return array.join(', ') + ', and ' + last; }; text += '|area=' + (params.indefinite ? 'certain ' : 'from certain '); if (params.pagerestrictions.length) { text += 'pages (' + makeSentence(params.pagerestrictions.map((p) => '[[:' + p + ']]')); text += params.namespacerestrictions.length ? ') and certain ' : ')'; } if (params.namespacerestrictions.length) { // 1 => Talk, 2 => User, etc. const namespaceNames = params.namespacerestrictions.map((id) => menuFormattedNamespaces[id]); text += '[[Wikipedia:Namespace|namespaces]] (' + makeSentence(namespaceNames) + ')'; } } else if (params.area) { text += '|area=' + params.area; } else { if (params.noemail) { text += '|email=yes'; } if (params.nocreate) { text += '|accountcreate=yes'; } } } } else { text += params.template; } if (settings.sig) { text += '|sig=' + settings.sig; } return text + '}}'; }; Twinkle.block.callback.main = function twinkleblockcallbackMain(pageobj) { const params = pageobj.getCallbackParameters(), date = new Morebits.Date(pageobj.getLoadTime()), messageData = params.messageData; let text; params.indefinite = Morebits.string.isInfinity(params.expiry); if (Twinkle.getPref('blankTalkpageOnIndefBlock') && params.template !== 'uw-lblock' && params.indefinite) { Morebits.Status.info('Info', 'Blanking talk page per preferences and creating a new talk page section for this month'); text = date.monthHeader() + '\n'; } else { text = pageobj.getPageText(); const dateHeaderRegex = date.monthHeaderRegex(); let dateHeaderRegexLast, dateHeaderRegexResult; while ((dateHeaderRegexLast = dateHeaderRegex.exec(text)) !== null) { dateHeaderRegexResult = dateHeaderRegexLast; } // If dateHeaderRegexResult is null then lastHeaderIndex is never checked. If it is not null but // \n== is not found, then the date header must be at the very start of the page. lastIndexOf // returns -1 in this case, so lastHeaderIndex gets set to 0 as desired. const lastHeaderIndex = text.lastIndexOf('\n==') + 1; if (text.length > 0) { text += '\n\n'; } if (!dateHeaderRegexResult || dateHeaderRegexResult.index !== lastHeaderIndex) { Morebits.Status.info('Info', 'Will create a new talk page section for this month, as none was found'); text += date.monthHeader() + '\n'; } } params.expiry = typeof params.template_expiry !== 'undefined' ? params.template_expiry : params.expiry; text += Twinkle.block.callback.getBlockNoticeWikitext(params); // build the edit summary let summary = messageData.summary; if (messageData.suppressArticleInSummary !== true && params.article) { summary += ' on [[:' + params.article + ']]'; } summary += '.'; pageobj.setPageText(text); pageobj.setEditSummary(summary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('watchWarnings')); pageobj.save(); }; Twinkle.addInitCallback(Twinkle.block, 'block'); }()); // </nowiki> 9jebteo6i0o0577azgtcyrsdystfm9e ಮಾದ್ಯಮೊವಿಕಿ:Gadget-twinkleconfig.js 8 25388 365985 357786 2026-06-24T06:31:18Z en>Novem Linguae 0 Repo at f08cefa: add rules for no space inside parentheses (#2341) 365985 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinkleconfig.js: Preferences module **************************************** * Mode of invocation: Adds configuration form to Wikipedia:Twinkle/Preferences, and adds an ad box to the top of user subpages belonging to the currently logged-in user which end in '.js' * Active on: What I just said. Yeah. */ Twinkle.config = {}; Twinkle.config.watchlistEnums = { yes: 'Add to watchlist (indefinitely)', no: "Don't add to watchlist", default: 'Follow your site preferences', '1 week': 'Watch for 1 week', '1 month': 'Watch for 1 month', '3 months': 'Watch for 3 months', '6 months': 'Watch for 6 months' }; Twinkle.config.commonSets = { csdCriteria: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', c4: 'C4', f1: 'F1', f2: 'F2', f3: 'F3', f7: 'F7', f8: 'F8', f9: 'F9', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5', g6: 'G6', g7: 'G7', g8: 'G8', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', t5: 'T5', u1: 'U1', u2: 'U2', u6: 'U6', u7: 'U7' }, csdCriteriaNotification: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', f1: 'F1', f2: 'F2', f3: 'F3', f7: 'F7', f9: 'F9', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5 ("general sanction violation" only)', g6: 'G6 ("copy-paste move" only)', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', u6: 'U6', u7: 'U7' }, csdAndImageDeletionCriteria: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', c4: 'C4', f1: 'F1', f2: 'F2', f3: 'F3', f4: 'F4', f5: 'F5', f6: 'F6', f7: 'F7', f8: 'F8', f9: 'F9', f11: 'F11', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5', g6: 'G6', g7: 'G7', g8: 'G8', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', t5: 'T5', u1: 'U1', u2: 'U2', u6: 'U6', u7: 'U7' }, namespacesNoSpecial: { 0: 'Article', 1: 'Talk (article)', 2: 'User', 3: 'User talk', 4: 'Wikipedia', 5: 'Wikipedia talk', 6: 'File', 7: 'File talk', 8: 'MediaWiki', 9: 'MediaWiki talk', 10: 'Template', 11: 'Template talk', 12: 'Help', 13: 'Help talk', 14: 'Category', 15: 'Category talk', 100: 'Portal', 101: 'Portal talk', 118: 'Draft', 119: 'Draft talk', 710: 'TimedText', 711: 'TimedText talk', 828: 'Module', 829: 'Module talk' } }; Twinkle.config.commonSets.csdCriteriaDisplayOrder = Object.keys(Twinkle.config.commonSets.csdCriteria); Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder = Object.keys(Twinkle.config.commonSets.csdCriteriaNotification); Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder = Object.keys(Twinkle.config.commonSets.csdAndImageDeletionCriteria); /** * Section entry format: * * { * title: <human-readable section title>, * module: <name of the associated module, used to link to sections>, * adminOnly: <true for admin-only sections>, * hidden: <true for advanced preferences that rarely need to be changed - they can still be modified by manually editing twinkleoptions.js>, * preferences: [ * { * name: <TwinkleConfig property name>, * label: <human-readable short description - used as a form label>, * helptip: <(optional) human-readable text (using valid HTML) that complements the description, like limits, warnings, etc.> * adminOnly: <true for admin-only preferences>, * type: <string|boolean|integer|enum|set|customList> (customList stores an array of JSON objects { value, label }), * enumValues: <for type = "enum": a JSON object where the keys are the internal names and the values are human-readable strings>, * setValues: <for type = "set": a JSON object where the keys are the internal names and the values are human-readable strings>, * setDisplayOrder: <(optional) for type = "set": an array containing the keys of setValues (as strings) in the order that they are displayed>, * customListValueTitle: <for type = "customList": the heading for the left "value" column in the custom list editor>, * customListLabelTitle: <for type = "customList": the heading for the right "label" column in the custom list editor> * }, * . . . * ] * }, * . . . * */ Twinkle.config.sections = [ { title: 'General', module: 'general', preferences: [ // TwinkleConfig.userTalkPageMode may take arguments: // 'window': open a new window, remember the opened window // 'tab': opens in a new tab, if possible. // 'blank': force open in a new window, even if such a window exists { name: 'userTalkPageMode', label: 'When opening a user talk page, open it', type: 'enum', enumValues: { window: 'In a window, replacing other user talks', tab: 'In a new tab', blank: 'In a totally new window' } }, // TwinkleConfig.dialogLargeFont (boolean) { name: 'dialogLargeFont', label: 'Use larger text in Twinkle dialogs', type: 'boolean' }, // Twinkle.config.disabledModules (array) { name: 'disabledModules', label: 'Turn off the selected Twinkle modules', helptip: 'Anything you select here will NOT be available for use, so act with care. Uncheck to reactivate.', type: 'set', setValues: { arv: 'ARV', warn: 'Warn', welcome: 'Welcome', talkback: 'Talkback', speedy: 'CSD', prod: 'PROD', xfd: 'XfD', image: 'Image (DI)', protect: 'Protect (RPP)', tag: 'Tag', diff: 'Diff', unlink: 'Unlink', rollback: 'Revert and rollback' } }, // Twinkle.config.disabledSysopModules (array) { name: 'disabledSysopModules', label: 'Turn off the selected admin-only modules', helptip: 'Anything you select here will NOT be available for use, so act with care. Uncheck to reactivate.', adminOnly: true, type: 'set', setValues: { block: 'Block', deprod: 'DePROD', batchdelete: 'D-batch', batchprotect: 'P-batch', batchundelete: 'Und-batch' } } ] }, { title: 'ARV', module: 'arv', preferences: [ { name: 'spiWatchReport', label: 'Add sockpuppet report pages to watchlist', type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Block user', module: 'block', adminOnly: true, preferences: [ // TwinkleConfig.defaultToBlock64 (boolean) // Whether to default to just blocking the /64 on or off { name: 'defaultToBlock64', label: 'For IPv6 addresses, select the option to block the /64 range by default', type: 'boolean' }, // TwinkleConfig.defaultToPartialBlocks (boolean) // Whether to default partial blocks on or off { name: 'defaultToPartialBlocks', label: 'Select partial blocks by default when opening the block menu', helptip: 'If the user is already blocked, this will be overridden in favor of defaulting to the current block type', type: 'boolean' }, // TwinkleConfig.blankTalkpageOnIndefBlock (boolean) // if true, blank the talk page when issuing an indef block notice (per [[WP:UWUL#Indefinitely blocked users]]) { name: 'blankTalkpageOnIndefBlock', label: 'Blank the talk page when indefinitely blocking users', helptip: 'See <a href="' + mw.util.getUrl('Wikipedia:WikiProject_User_warnings/Usage_and_layout#Indefinitely_blocked_users') + '">WP:UWUL</a> for more information.', type: 'boolean' } ] }, { title: 'Image deletion (DI)', module: 'image', preferences: [ // TwinkleConfig.notifyUserOnDeli (boolean) // If the user should be notified after placing a file deletion tag { name: 'notifyUserOnDeli', label: 'Check the "notify initial uploader" box by default', type: 'boolean' }, // TwinkleConfig.deliWatchPage (string) // The watchlist setting of the page tagged for deletion. { name: 'deliWatchPage', label: 'Add image page to watchlist when tagging', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.deliWatchUser (string) // The watchlist setting of the user talk page if a notification is placed. { name: 'deliWatchUser', label: 'Add user talk page of initial uploader to watchlist when notifying', type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Page protection ' + (Morebits.userIsSysop ? '(PP)' : '(RPP)'), module: 'protect', preferences: [ { name: 'watchRequestedPages', label: 'Add page to watchlist when requesting protection', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchPPTaggedPages', label: 'Add page to watchlist when tagging with protection template', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchProtectedPages', label: 'Add page to watchlist when protecting', helptip: 'If also tagging the page after protection, that preference will be favored.', adminOnly: true, type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Proposed deletion (PROD)', module: 'prod', preferences: [ // TwinkleConfig.watchProdPages (string) // Watchlist setting when applying prod template to page { name: 'watchProdPages', label: 'Add article to watchlist when tagging', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.markProdPagesAsPatrolled (boolean) // If, when applying prod template to page, to mark the page as curated/patrolled (if the page was reached from NewPages) { name: 'markProdPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when tagging (if possible)', helptip: 'This should probably not be checked as doing so is against best practice consensus', type: 'boolean' }, // TwinkleConfig.prodReasonDefault (string) // The prefilled PROD reason. { name: 'prodReasonDefault', label: 'Prefilled PROD reason', type: 'string' }, { name: 'logProdPages', label: 'Keep a log in userspace of all pages you tag for PROD', helptip: 'Since non-admins do not have access to their deleted contributions, the userspace log offers a good way to keep track of all pages you tag for PROD using Twinkle.', type: 'boolean' }, { name: 'prodLogPageName', label: 'Keep the PROD userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your PROD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the PROD userspace log.', type: 'string' } ] }, { title: 'Revert and rollback', module: 'rollback', preferences: [ // TwinkleConfig.autoMenuAfterRollback (bool) // Option to automatically open the warning menu if the user talk page is opened post-reversion { name: 'autoMenuAfterRollback', label: 'Automatically open the Twinkle warn menu on a user talk page after Twinkle rollback', helptip: 'Only operates if the relevant box is checked below.', type: 'boolean' }, // TwinkleConfig.openTalkPage (array) // What types of actions that should result in opening of talk page { name: 'openTalkPage', label: 'Open user talk page after these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback' } }, // TwinkleConfig.openTalkPageOnAutoRevert (bool) // Defines if talk page should be opened when calling revert from contribs or recent changes pages. If set to true, openTalkPage defines then if talk page will be opened. { name: 'openTalkPageOnAutoRevert', label: 'Open user talk page when invoking rollback from user contributions or recent changes', helptip: 'When this is on, the desired options must be enabled in the previous setting for this to work.', type: 'boolean' }, // TwinkleConfig.rollbackInPlace (bool) // { name: 'rollbackInPlace', label: "Don't reload the page when rolling back from contributions or recent changes", helptip: "When this is on, Twinkle won't reload the contributions or recent changes feed after reverting, allowing you to revert more than one edit at a time.", type: 'boolean' }, // TwinkleConfig.markRevertedPagesAsMinor (array) // What types of actions that should result in marking edit as minor { name: 'markRevertedPagesAsMinor', label: 'Mark as minor edit for these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback', torev: '"Restore this version"' } }, // TwinkleConfig.watchRevertedPages (array) // What types of actions that should result in forced addition to watchlist { name: 'watchRevertedPages', label: 'Add pages to watchlist for these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback', torev: '"Restore this version"' } }, // TwinkleConfig.watchRevertedExpiry // If any of the above items are selected, whether to expire the watch { name: 'watchRevertedExpiry', label: 'When reverting a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.offerReasonOnNormalRevert (boolean) // If to offer a prompt for extra summary reason for normal reverts, default to true { name: 'offerReasonOnNormalRevert', label: 'Prompt for reason for normal rollbacks', helptip: '"Normal" rollbacks are the ones that are invoked from the middle [rollback] link.', type: 'boolean' }, { name: 'confirmOnRollback', label: 'Require confirmation before reverting (all devices)', helptip: 'For users of pen or touch devices, and chronically indecisive people.', type: 'boolean' }, { name: 'confirmOnMobileRollback', label: 'Require confirmation before reverting (mobile devices only)', helptip: 'Avoid accidental reversions when on mobile devices.', type: 'boolean' }, // TwinkleConfig.showRollbackLinks (array) // Where Twinkle should show rollback links: // diff, others, mine, contribs, history, recent // Note from TTO: |contribs| seems to be equal to |others| + |mine|, i.e. redundant, so I left it out heres { name: 'showRollbackLinks', label: 'Show rollback links on these pages', type: 'set', setValues: { diff: 'Diff pages', others: 'Contributions pages of other users', mine: 'My contributions page', recent: 'Recent changes and related changes special pages', history: 'History pages' } } ] }, { title: 'Speedy deletion (CSD)', module: 'speedy', preferences: [ { name: 'speedySelectionStyle', label: 'When to go ahead and tag/delete the page', type: 'enum', enumValues: { buttonClick: 'When I click "Submit"', radioClick: 'As soon as I click an option' } }, // TwinkleConfig.watchSpeedyPages (array) // Whether to add speedy tagged or deleted pages to watchlist { name: 'watchSpeedyPages', label: 'Add page to watchlist when using these criteria', type: 'set', setValues: Twinkle.config.commonSets.csdCriteria, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder }, // TwinkleConfig.watchSpeedyExpiry // If any of the above items are selected, whether to expire the watch { name: 'watchSpeedyExpiry', label: 'When tagging a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.markSpeedyPagesAsPatrolled (boolean) // If, when applying speedy template to page, to mark the page as triaged/patrolled (if the page was reached from NewPages) { name: 'markSpeedyPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when tagging (if possible)', helptip: 'This should probably not be checked as doing so is against best practice consensus', type: 'boolean' }, // TwinkleConfig.watchSpeedyUser (string) // The watchlist setting of the user talk page if they receive a notification. { name: 'watchSpeedyUser', label: 'Add user talk page of initial contributor to watchlist (when notifying)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.welcomeUserOnSpeedyDeletionNotification (array of strings) // On what types of speedy deletion notifications shall the user be welcomed // with a "firstarticle" notice if their talk page has not yet been created. { name: 'welcomeUserOnSpeedyDeletionNotification', label: 'Welcome page creator when notifying with these criteria', helptip: 'The welcome is issued only if the user is notified about the deletion, and only if their talk page does not already exist. The template used is {{firstarticle}}.', type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.notifyUserOnSpeedyDeletionNomination (array) // What types of actions should result in the author of the page being notified of nomination { name: 'notifyUserOnSpeedyDeletionNomination', label: 'Notify page creator when tagging with these criteria', helptip: 'Even if you choose to notify from the CSD screen, the notification will only take place for those criteria selected here.', type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.warnUserOnSpeedyDelete (array) // What types of actions should result in the author of the page being notified of speedy deletion (admin only) { name: 'warnUserOnSpeedyDelete', label: 'Notify page creator when deleting under these criteria', helptip: 'Even if you choose to notify from the CSD screen, the notification will only take place for those criteria selected here.', adminOnly: true, type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.promptForSpeedyDeletionSummary (array of strings) { name: 'promptForSpeedyDeletionSummary', label: 'Allow editing of deletion summary when deleting under these criteria', adminOnly: true, type: 'set', setValues: Twinkle.config.commonSets.csdAndImageDeletionCriteria, setDisplayOrder: Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder }, // TwinkleConfig.deleteTalkPageOnDelete (boolean) // If talk page if exists should also be deleted (CSD G8) when spedying a page (admin only) { name: 'deleteTalkPageOnDelete', label: 'Check the "also delete talk page" box by default', adminOnly: true, type: 'boolean' }, { name: 'deleteRedirectsOnDelete', label: 'Check the "also delete redirects" box by default', adminOnly: true, type: 'boolean' }, // TwinkleConfig.deleteSysopDefaultToDelete (boolean) // Make the CSD screen default to "delete" instead of "tag" (admin only) { name: 'deleteSysopDefaultToDelete', label: 'Default to outright deletion instead of speedy tagging', helptip: 'If there is a CSD tag already present, Twinkle will always default to "delete" mode', adminOnly: true, type: 'boolean' }, // TwinkleConfig.speedyWindowWidth (integer) // Defines the width of the Twinkle SD window in pixels { name: 'speedyWindowWidth', label: 'Width of speedy deletion window (pixels)', type: 'integer' }, // TwinkleConfig.speedyWindowWidth (integer) // Defines the width of the Twinkle SD window in pixels { name: 'speedyWindowHeight', label: 'Height of speedy deletion window (pixels)', helptip: 'If you have a big monitor, you might like to increase this.', type: 'integer' }, { name: 'logSpeedyNominations', label: 'Keep a log in userspace of all CSD nominations', helptip: 'Since non-admins do not have access to their deleted contributions, the userspace log offers a good way to keep track of all pages you nominate for CSD using Twinkle. Files tagged using DI are also added to this log.', type: 'boolean' }, { name: 'speedyLogPageName', label: 'Keep the CSD userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your CSD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the CSD userspace log.', type: 'string' }, { name: 'noLogOnSpeedyNomination', label: 'Do not create a userspace log entry when tagging with these criteria', type: 'set', setValues: Twinkle.config.commonSets.csdAndImageDeletionCriteria, setDisplayOrder: Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder } ] }, { title: 'Tag', module: 'tag', preferences: [ { name: 'watchTaggedVenues', label: 'Add page to watchlist when tagging these type of pages', type: 'set', setValues: { articles: 'Articles', drafts: 'Drafts', redirects: 'Redirects', files: 'Files' } }, { name: 'watchTaggedPages', label: 'When tagging a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchMergeDiscussions', label: 'Add talk pages to watchlist when starting merge discussions', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'markTaggedPagesAsMinor', label: 'Mark addition of tags as a minor edit', type: 'boolean' }, { name: 'markTaggedPagesAsPatrolled', label: 'Check the "mark page as patrolled/reviewed" box by default', type: 'boolean' }, { name: 'groupByDefault', label: 'Check the "group into {{multiple issues}}" box by default', type: 'boolean' }, { name: 'tagArticleSortOrder', label: 'Default view order for article tags', type: 'enum', enumValues: { cat: 'By categories', alpha: 'In alphabetical order' } }, { name: 'customTagList', label: 'Custom article/draft maintenance tags to display', helptip: "These appear as additional options at the bottom of the list of tags. For example, you could add new maintenance tags which have not yet been added to Twinkle's defaults.", type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' }, { name: 'customFileTagList', label: 'Custom file maintenance tags to display', helptip: 'Additional tags that you wish to add for files.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' }, { name: 'customRedirectTagList', label: 'Custom redirect category tags to display', helptip: 'Additional tags that you wish to add for redirects.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' } ] }, { title: 'Talkback', module: 'talkback', preferences: [ { name: 'markTalkbackAsMinor', label: 'Mark talkbacks as minor edits', type: 'boolean' }, { name: 'insertTalkbackSignature', label: 'Insert signature within talkbacks', type: 'boolean' }, { name: 'talkbackHeading', label: 'Section heading to use for talkback and please see', tooltip: 'Should NOT include the equals signs ("==") used for wikitext formatting', type: 'string' }, { name: 'mailHeading', label: "Section heading to use for \"you've got mail\" notices", tooltip: 'Should NOT include the equals signs ("==") used for wikitext formatting', type: 'string' } ] }, { title: 'Unlink', module: 'unlink', preferences: [ // TwinkleConfig.unlinkNamespaces (array) // In what namespaces unlink should happen, default in 0 (article), 10 (template), 100 (portal), and 118 (draft) { name: 'unlinkNamespaces', label: 'Remove links from pages in these namespaces', helptip: 'Avoid selecting any talk namespaces, as Twinkle might end up unlinking on talk archives (a big no-no).', type: 'set', setValues: Twinkle.config.commonSets.namespacesNoSpecial } ] }, { title: 'Warn user', module: 'warn', preferences: [ // TwinkleConfig.defaultWarningGroup (int) // Which level warning should be the default selected group, default is 1 { name: 'defaultWarningGroup', label: 'Default warning level', type: 'enum', enumValues: { 1: 'Level 1', 2: 'Level 2', 3: 'Level 3', 4: 'Level 4', 5: 'Level 4im', 6: 'Single-issue notices', 7: 'Single-issue warnings', // 8 was used for block templates before #260 9: 'Custom warnings', 10: 'All warning templates', 11: 'Auto-select level (1-4)' } }, // TwinkleConfig.combinedSingletMenus (boolean) // if true, show one menu with both single-issue notices and warnings instead of two separately { name: 'combinedSingletMenus', label: 'Replace the two separate single-issue menus into one combined menu', helptip: 'Selecting either single-issue notices or single-issue warnings as your default will make this your default if enabled.', type: 'boolean' }, // TwinkleConfig.watchWarnings (string) // Watchlist setting for the page which has been dispatched an warning or notice { name: 'watchWarnings', label: 'Add user talk page to watchlist when notifying', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.oldSelect (boolean) // if true, use the native select menu rather the select2-based one { name: 'oldSelect', label: 'Use the non-searchable classic select menu', type: 'boolean' }, { name: 'customWarningList', label: 'Custom warning templates to display', helptip: 'You can add individual templates or user subpages. Custom warnings appear in the "Custom warnings" category within the warning dialog box.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in warning list (also used as edit summary)' } ] }, { title: 'Welcome user', module: 'welcome', preferences: [ { name: 'topWelcomes', label: 'Place welcomes above existing content on user talk pages', type: 'boolean' }, { name: 'watchWelcomes', label: 'Add user talk pages to watchlist when welcoming', helptip: 'Doing so adds to the personal element of welcoming a user - you will be able to see how they are coping as a newbie, and possibly help them.', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'insertUsername', label: 'Add your username to the template (where applicable)', helptip: "Some welcome templates have an opening sentence like \"Hi, I'm &lt;username&gt;. Welcome\" etc. If you turn off this option, these templates will not display your username in that way.", type: 'boolean' }, { name: 'quickWelcomeMode', label: 'Clicking the "welcome" link on a diff page (which only appears if the editor\'s user talk page has not been created yet) will', helptip: 'If you choose to welcome automatically, the template you specify below will be used.', type: 'enum', enumValues: { auto: 'immediately post the welcome template specified below', norm: 'prompt you to select a template' } }, { name: 'quickWelcomeTemplate', label: 'Template to use when welcoming automatically', helptip: 'Enter the name of a welcome template, without the curly brackets. A link to the given article will be added.', type: 'string' }, { name: 'customWelcomeList', label: 'Custom welcome templates to display', helptip: "You can add other welcome templates, or user subpages that are welcome templates (prefixed with \"User:\"). Don't forget that these templates are substituted onto user talk pages.", type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Welcome dialog' }, { name: 'customWelcomeSignature', label: 'Automatically sign custom welcome templates', helptip: 'If your custom welcome templates contain a built-in signature within the template, turn off this option.', type: 'boolean' } ] }, { title: 'XFD (deletion discussions)', module: 'xfd', preferences: [ { name: 'logXfdNominations', label: 'Keep a log in userspace of all pages you nominate for a deletion discussion (XfD)', helptip: 'The userspace log offers a good way to keep track of all pages you nominate for XfD using Twinkle.', type: 'boolean' }, { name: 'xfdLogPageName', label: 'Keep the deletion discussion userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your XfD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the XfD userspace log.', type: 'string' }, { name: 'noLogOnXfdNomination', label: 'Do not create a userspace log entry when nominating at this venue', type: 'set', setValues: { afd: 'AfD', tfd: 'TfD', ffd: 'FfD', cfd: 'CfD', cfds: 'CfD/S', mfd: 'MfD', rfd: 'RfD', rm: 'RM' } }, // TwinkleConfig.xfdWatchPage (string) // The watchlist setting of the page being nominated for XfD. { name: 'xfdWatchPage', label: 'Add the nominated page to watchlist', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchDiscussion (string) // The watchlist setting of the newly created XfD page (for those processes that create discussion pages for each nomination), // or the list page for the other processes. { name: 'xfdWatchDiscussion', label: 'Add the deletion discussion page to watchlist', helptip: 'This refers to the discussion subpage (for AfD and MfD) or the daily log page (for TfD, CfD, RfD and FfD)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchList (string) // The watchlist setting of the XfD list page, *if* the discussion is on a separate page. { name: 'xfdWatchList', label: 'Add the daily log/list page to the watchlist (AfD and MfD)', helptip: 'This only applies for AfD and MfD, where the discussions are transcluded onto a daily log page (for AfD) or the main MfD page (for MfD).', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchUser (string) // The watchlist setting of the user talk page if they receive a notification. { name: 'xfdWatchUser', label: 'Add user talk page of initial contributor to watchlist (when notifying)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchRelated (string) // The watchlist setting of the target of a redirect being nominated for RfD. { name: 'xfdWatchRelated', label: "Add the redirect's target page to watchlist (when notifying)", helptip: 'This only applies for RfD, when leaving a notification on the talk page of the target of the redirect', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'markXfdPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when nominating for AFD (if possible)', type: 'boolean' } ] }, { title: 'Hidden', hidden: true, preferences: [ // twinklerollback.js: defines how many revision to query maximum, maximum possible is 50, default is 50 { name: 'revertMaxRevisions', type: 'integer' }, // twinklewarn.js: When using the autolevel select option, how many days makes a prior warning stale // Huggle is three days ([[Special:Diff/918980316]] and [[Special:Diff/919417999]]) while ClueBotNG is two: // https://github.com/DamianZaremba/cluebotng/blob/4958e25d6874cba01c75f11debd2e511fd5a2ce5/bot/action_functions.php#L62 { name: 'autolevelStaleDays', type: 'integer' }, // How many pages should be queried by deprod and batchdelete/protect/undelete { name: 'batchMax', type: 'integer', adminOnly: true }, // How many pages should be processed at a time by deprod and batchdelete/protect/undelete { name: 'batchChunks', type: 'integer', adminOnly: true } ] } ]; // end of Twinkle.config.sections Twinkle.config.init = function twinkleconfigInit() { // create the config page at Wikipedia:Twinkle/Preferences if ((mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').project && mw.config.get('wgTitle') === 'Twinkle/Preferences') && mw.config.get('wgAction') === 'view') { if (!document.getElementById('twinkle-config')) { return; // maybe the page is misconfigured, or something - but any attempt to modify it will be pointless } // set style to nothing to prevent conflict with external css document.getElementById('twinkle-config').removeAttribute('style'); document.getElementById('twinkle-config-titlebar').removeAttribute('style'); const contentdiv = document.getElementById('twinkle-config-content'); contentdiv.textContent = ''; // clear children // let user know about possible conflict with skin js/common.js file // (settings in that file will still work, but they will be overwritten by twinkleoptions.js settings) if (window.TwinkleConfig || window.FriendlyConfig) { const contentnotice = document.createElement('div'); contentnotice.className = 'plainlinks twinkle-ombox'; contentnotice.innerHTML = '<div>' + '<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/3/38/Imbox_content.png" />' + '</div>' + '<div>' + '<p><big><b>Before modifying your settings here,</b> you must remove your old Twinkle and Friendly settings from your personal skin JavaScript.</big></p>' + '<p>To do this, you can <a href="' + mw.util.getUrl('User:' + mw.config.get('wgUserName') + '/' + mw.config.get('skin') + '.js', { action: 'edit' }) + '" target="_blank"><b>edit your personal skin javascript file</b></a> or <a href="' + mw.util.getUrl('User:' + mw.config.get('wgUserName') + '/common.js', { action: 'edit'}) + '" target="_blank"><b>your common.js file</b></a>, removing all lines of code that refer to <code>TwinkleConfig</code> and <code>FriendlyConfig</code>.</p>' + '</div>'; contentdiv.appendChild(contentnotice); } // start a table of contents const toctable = document.createElement('div'); toctable.className = 'toc'; toctable.style.marginLeft = '0.4em'; // create TOC title const toctitle = document.createElement('div'); toctitle.id = 'toctitle'; const toch2 = document.createElement('h2'); toch2.textContent = 'Contents '; toctitle.appendChild(toch2); // add TOC show/hide link const toctoggle = document.createElement('span'); toctoggle.className = 'toctoggle'; toctoggle.appendChild(document.createTextNode('[')); const toctogglelink = document.createElement('a'); toctogglelink.className = 'internal'; toctogglelink.setAttribute('href', '#tw-tocshowhide'); toctogglelink.textContent = 'hide'; toctoggle.appendChild(toctogglelink); toctoggle.appendChild(document.createTextNode(']')); toctitle.appendChild(toctoggle); toctable.appendChild(toctitle); // create item container: this is what we add stuff to const tocul = document.createElement('ul'); toctogglelink.addEventListener('click', () => { const $tocul = $(tocul); $tocul.toggle(); if ($tocul.find(':visible').length) { toctogglelink.textContent = 'hide'; } else { toctogglelink.textContent = 'show'; } }, false); toctable.appendChild(tocul); contentdiv.appendChild(toctable); const contentform = document.createElement('form'); contentform.setAttribute('action', 'javascript:void(0)'); // was #tw-save - changed to void(0) to work around Chrome issue contentform.addEventListener('submit', Twinkle.config.save, true); contentdiv.appendChild(contentform); const container = document.createElement('table'); container.style.width = '100%'; contentform.appendChild(container); $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // i.e. "continue" in this context } // add to TOC const tocli = document.createElement('li'); tocli.className = 'toclevel-1'; const toca = document.createElement('a'); toca.setAttribute('href', '#' + section.module); toca.appendChild(document.createTextNode(section.title)); tocli.appendChild(toca); tocul.appendChild(tocli); let row = document.createElement('tr'); let cell = document.createElement('td'); cell.setAttribute('colspan', '3'); const heading = document.createElement('h4'); heading.style.borderBottom = '1px solid gray'; heading.style.marginTop = '0.2em'; heading.id = section.module; heading.appendChild(document.createTextNode(section.title)); cell.appendChild(heading); row.appendChild(cell); container.appendChild(row); let rowcount = 1; // for row banding // add each of the preferences to the form $(section.preferences).each((prefkey, pref) => { if (pref.adminOnly && !Morebits.userIsSysop) { return true; // i.e. "continue" in this context } row = document.createElement('tr'); row.style.marginBottom = '0.2em'; // create odd row banding if (rowcount++ % 2 === 0) { row.style.backgroundColor = 'rgba(128, 128, 128, 0.1)'; } cell = document.createElement('td'); let label, input; const gotPref = Twinkle.getPref(pref.name); switch (pref.type) { case 'boolean': // create a checkbox cell.setAttribute('colspan', '2'); label = document.createElement('label'); input = document.createElement('input'); input.setAttribute('type', 'checkbox'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); if (gotPref === true) { input.setAttribute('checked', 'checked'); } label.appendChild(input); label.appendChild(document.createTextNode(pref.label)); cell.appendChild(label); break; case 'string': // create an input box case 'integer': // add label to first column cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add input box to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; input = document.createElement('input'); input.setAttribute('type', 'text'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); if (pref.type === 'integer') { input.setAttribute('size', 6); input.setAttribute('type', 'number'); input.setAttribute('step', '1'); // integers only } if (gotPref) { input.setAttribute('value', gotPref); } cell.appendChild(input); break; case 'enum': // create a combo box // add label to first column // note: duplicates the code above, under string/integer cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add input box to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; input = document.createElement('select'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); $.each(pref.enumValues, (enumvalue, enumdisplay) => { const option = document.createElement('option'); option.setAttribute('value', enumvalue); if ((gotPref === enumvalue) || // Hack to convert old boolean watchlist prefs // to corresponding enums (added in v2.1) (typeof gotPref === 'boolean' && ((gotPref && enumvalue === 'yes') || (!gotPref && enumvalue === 'no')))) { option.setAttribute('selected', 'selected'); } option.appendChild(document.createTextNode(enumdisplay)); input.appendChild(option); }); cell.appendChild(input); break; case 'set': // create a set of check boxes // add label first of all cell.setAttribute('colspan', '2'); label = document.createElement('label'); // not really necessary to use a label element here, but we do it for consistency of styling label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); var checkdiv = document.createElement('div'); checkdiv.style.paddingLeft = '1em'; var worker = function(itemkey, itemvalue) { const checklabel = document.createElement('label'); checklabel.style.marginRight = '0.7em'; checklabel.style.display = 'inline-block'; const check = document.createElement('input'); check.setAttribute('type', 'checkbox'); check.setAttribute('id', pref.name + '_' + itemkey); check.setAttribute('name', pref.name + '_' + itemkey); if (gotPref && gotPref.includes(itemkey)) { check.setAttribute('checked', 'checked'); } // cater for legacy integer array values for unlinkNamespaces (this can be removed a few years down the track...) if (pref.name === 'unlinkNamespaces') { if (gotPref && gotPref.includes(parseInt(itemkey, 10))) { check.setAttribute('checked', 'checked'); } } checklabel.appendChild(check); checklabel.appendChild(document.createTextNode(itemvalue)); checkdiv.appendChild(checklabel); }; if (pref.setDisplayOrder) { // add check boxes according to the given display order $.each(pref.setDisplayOrder, (itemkey, item) => { worker(item, pref.setValues[item]); }); } else { // add check boxes according to the order it gets fed to us (probably strict alphabetical) $.each(pref.setValues, worker); } cell.appendChild(checkdiv); break; case 'customList': // add label to first column cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add button to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; var button = document.createElement('button'); button.setAttribute('id', pref.name); button.setAttribute('name', pref.name); button.setAttribute('type', 'button'); button.addEventListener('click', Twinkle.config.listDialog.display, false); // use jQuery data on the button to store the current config value $(button).data({ value: gotPref, pref: pref }); button.appendChild(document.createTextNode('Edit items')); cell.appendChild(button); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } row.appendChild(cell); // add help tip cell = document.createElement('td'); cell.className = 'twinkle-config-helptip'; if (pref.helptip) { // convert mentions of templates in the helptip to clickable links cell.innerHTML = pref.helptip.replace(/{{(.+?)}}/g, '{{<a href="' + mw.util.getUrl('Template:') + '$1" target="_blank">$1</a>}}'); } // add reset link (custom lists don't need this, as their config value isn't displayed on the form) if (pref.type !== 'customList') { const resetlink = document.createElement('a'); resetlink.setAttribute('href', '#tw-reset'); resetlink.setAttribute('id', 'twinkle-config-reset-' + pref.name); resetlink.addEventListener('click', Twinkle.config.resetPrefLink, false); resetlink.style.cssFloat = 'right'; resetlink.style.margin = '0 0.6em'; resetlink.appendChild(document.createTextNode('Reset')); cell.appendChild(resetlink); } row.appendChild(cell); container.appendChild(row); return true; }); return true; }); const footerbox = document.createElement('div'); footerbox.setAttribute('id', 'twinkle-config-buttonpane'); const button = document.createElement('button'); button.setAttribute('id', 'twinkle-config-submit'); button.setAttribute('type', 'submit'); button.appendChild(document.createTextNode('Save changes')); footerbox.appendChild(button); const footerspan = document.createElement('span'); footerspan.className = 'plainlinks'; footerspan.style.marginLeft = '2.4em'; footerspan.style.fontSize = '90%'; const footera = document.createElement('a'); footera.setAttribute('href', '#tw-reset-all'); footera.setAttribute('id', 'twinkle-config-resetall'); footera.addEventListener('click', Twinkle.config.resetAllPrefs, false); footera.appendChild(document.createTextNode('Restore defaults')); footerspan.appendChild(footera); footerbox.appendChild(footerspan); contentform.appendChild(footerbox); // since all the section headers exist now, we can try going to the requested anchor if (window.location.hash) { const loc = window.location.hash; window.location.hash = ''; window.location.hash = loc; } } else if (mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').user && mw.config.get('wgTitle').indexOf(mw.config.get('wgUserName')) === 0 && mw.config.get('wgPageName').slice(-3) === '.js') { const box = document.createElement('div'); // Styled in twinkle.css box.setAttribute('id', 'twinkle-config-headerbox'); let link; const scriptPageName = mw.config.get('wgPageName').slice( mw.config.get('wgPageName').lastIndexOf('/') + 1, mw.config.get('wgPageName').lastIndexOf('.js') ); if (scriptPageName === 'twinkleoptions') { // place "why not try the preference panel" notice box.setAttribute('class', 'config-twopt-box'); if (mw.config.get('wgArticleId') > 0) { // page exists box.appendChild(document.createTextNode('This page contains your Twinkle preferences. You can change them using the ')); } else { // page does not exist box.appendChild(document.createTextNode('You can customize Twinkle to suit your preferences by using the ')); } link = document.createElement('a'); link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':Twinkle/Preferences')); link.appendChild(document.createTextNode('Twinkle preferences panel')); box.appendChild(link); box.appendChild(document.createTextNode(', or by editing this page.')); $(box).insertAfter($('#contentSub')); } else if (['monobook', 'vector', 'vector-2022', 'cologneblue', 'modern', 'timeless', 'minerva', 'common'].includes(scriptPageName)) { // place "Looking for Twinkle options?" notice box.setAttribute('class', 'config-userskin-box'); box.appendChild(document.createTextNode('If you want to set Twinkle preferences, you can use the ')); link = document.createElement('a'); link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':Twinkle/Preferences')); link.appendChild(document.createTextNode('Twinkle preferences panel')); box.appendChild(link); box.appendChild(document.createTextNode('.')); $(box).insertAfter($('#contentSub')); } } }; // custom list-related stuff Twinkle.config.listDialog = {}; Twinkle.config.listDialog.addRow = function twinkleconfigListDialogAddRow($dlgtable, value, label) { let $contenttr, $valueInput, $labelInput; $dlgtable.append( $contenttr = $('<tr>').append( $('<td>').append( $('<button>') .attr('type', 'button') .on('click', () => { $contenttr.remove(); }) .text('Remove') ), $('<td>').append( $valueInput = $('<input>') .attr('type', 'text') .addClass('twinkle-config-customlist-value') .css('width', '97%') ), $('<td>').append( $labelInput = $('<input>') .attr('type', 'text') .addClass('twinkle-config-customlist-label') .css('width', '98%') ) ) ); if (value) { $valueInput.val(value); } if (label) { $labelInput.val(label); } }; Twinkle.config.listDialog.display = function twinkleconfigListDialogDisplay(e) { const $prefbutton = $(e.target); const curvalue = $prefbutton.data('value'); const curpref = $prefbutton.data('pref'); const dialog = new Morebits.SimpleWindow(720, 400); dialog.setTitle(curpref.label); dialog.setScriptName('Twinkle preferences'); let $dlgtbody; dialog.setContent( $('<div>').append( $('<table>') .addClass('wikitable') .css({ margin: '1.4em 1em', width: 'auto' }) .append( $dlgtbody = $('<tbody>').append( // header row $('<tr>').append( $('<th>') // top-left cell .css('width', '5%'), $('<th>') // value column header .css('width', '35%') .text(curpref.customListValueTitle ? curpref.customListValueTitle : 'Value'), $('<th>') // label column header .css('width', '60%') .text(curpref.customListLabelTitle ? curpref.customListLabelTitle : 'Label') ) ), $('<tfoot>').append( $('<tr>').append( $('<td>') .attr('colspan', '3') .append( $('<button>') .text('Add') .css('min-width', '8em') .attr('type', 'button') .on('click', () => { Twinkle.config.listDialog.addRow($dlgtbody); }) ) ) ) ), $('<button>') .text('Save changes') .attr('type', 'submit') // so Morebits.SimpleWindow puts the button in the button pane .on('click', () => { Twinkle.config.listDialog.save($prefbutton, $dlgtbody); dialog.close(); }), $('<button>') .text('Reset') .attr('type', 'submit') .on('click', () => { Twinkle.config.listDialog.reset($prefbutton, $dlgtbody); }), $('<button>') .text('Cancel') .attr('type', 'submit') .on('click', () => { dialog.close(); }) )[0] ); // content rows let gotRow = false; $.each(curvalue, (k, v) => { gotRow = true; Twinkle.config.listDialog.addRow($dlgtbody, v.value, v.label); }); // if there are no values present, add a blank row to start the user off if (!gotRow) { Twinkle.config.listDialog.addRow($dlgtbody); } dialog.display(); }; // Resets the data value, re-populates based on the new (default) value, then saves the // old data value again (less surprising behaviour) Twinkle.config.listDialog.reset = function twinkleconfigListDialogReset($button, $tbody) { // reset value on button const curpref = $button.data('pref'); const oldvalue = $button.data('value'); Twinkle.config.resetPref(curpref); // reset form $tbody.find('tr').slice(1).remove(); // all rows except the first (header) row // add the new values const curvalue = $button.data('value'); $.each(curvalue, (k, v) => { Twinkle.config.listDialog.addRow($tbody, v.value, v.label); }); // save the old value $button.data('value', oldvalue); }; Twinkle.config.listDialog.save = function twinkleconfigListDialogSave($button, $tbody) { const result = []; let current = {}; $tbody.find('input[type="text"]').each((inputkey, input) => { if ($(input).hasClass('twinkle-config-customlist-value')) { current = { value: input.value }; } else { current.label = input.value; // exclude totally empty rows if (current.value || current.label) { result.push(current); } } }); $button.data('value', result); }; // reset/restore defaults Twinkle.config.resetPrefLink = function twinkleconfigResetPrefLink(e) { const wantedpref = e.target.id.slice(21); // "twinkle-config-reset-" prefix is stripped // search tactics $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // continue: skip impossibilities } let foundit = false; $(section.preferences).each((prefkey, pref) => { if (pref.name !== wantedpref) { return true; // continue } Twinkle.config.resetPref(pref); foundit = true; return false; // break }); if (foundit) { return false; // break } }); return false; // stop link from scrolling page }; Twinkle.config.resetPref = function twinkleconfigResetPref(pref) { switch (pref.type) { case 'boolean': document.getElementById(pref.name).checked = Twinkle.defaultConfig[pref.name]; break; case 'string': case 'integer': case 'enum': document.getElementById(pref.name).value = Twinkle.defaultConfig[pref.name]; break; case 'set': $.each(pref.setValues, (itemkey) => { if (document.getElementById(pref.name + '_' + itemkey)) { document.getElementById(pref.name + '_' + itemkey).checked = Twinkle.defaultConfig[pref.name].includes(itemkey); } }); break; case 'customList': $(document.getElementById(pref.name)).data('value', Twinkle.defaultConfig[pref.name]); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } }; Twinkle.config.resetAllPrefs = function twinkleconfigResetAllPrefs() { // no confirmation message - the user can just refresh/close the page to abort $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // continue: skip impossibilities } $(section.preferences).each((prefkey, pref) => { if (!pref.adminOnly || Morebits.userIsSysop) { Twinkle.config.resetPref(pref); } }); return true; }); return false; // stop link from scrolling page }; Twinkle.config.save = function twinkleconfigSave(e) { Morebits.Status.init(document.getElementById('twinkle-config-content')); const userjs = mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').user] + ':' + mw.config.get('wgUserName') + '/twinkleoptions.js'; const wikipediaPage = new Morebits.wiki.Page(userjs, 'Saving preferences to ' + userjs); wikipediaPage.setCallbackParameters(e.target); wikipediaPage.load(Twinkle.config.writePrefs); return false; }; Twinkle.config.writePrefs = function twinkleconfigWritePrefs(pageobj) { const form = pageobj.getCallbackParameters(); // this is the object which gets serialized into JSON; only // preferences that this script knows about are kept const newConfig = {optionsVersion: 2.1}; // a comparison function is needed later on // it is just enough for our purposes (i.e. comparing strings, numbers, booleans, // arrays of strings, and arrays of { value, label }) // and it is not very robust: e.g. compare([2], ["2"]) === true, and // compare({}, {}) === false, but it's good enough for our purposes here const compare = function(a, b) { if (Array.isArray(a)) { if (a.length !== b.length) { return false; } const asort = a.sort(), bsort = b.sort(); for (let i = 0; asort[i]; ++i) { // comparison of the two properties of custom lists if ((typeof asort[i] === 'object') && (asort[i].label !== bsort[i].label || asort[i].value !== bsort[i].value)) { return false; } else if (asort[i].toString() !== bsort[i].toString()) { return false; } } return true; } return a === b; }; $(Twinkle.config.sections).each((sectionkey, section) => { if (section.adminOnly && !Morebits.userIsSysop) { return; // i.e. "continue" in this context } // reach each of the preferences from the form $(section.preferences).each((prefkey, pref) => { let userValue; // = undefined // only read form values for those prefs that have them if (!pref.adminOnly || Morebits.userIsSysop) { if (!section.hidden) { switch (pref.type) { case 'boolean': // read from the checkbox userValue = form[pref.name].checked; break; case 'string': // read from the input box or combo box case 'enum': userValue = form[pref.name].value; break; case 'integer': // read from the input box userValue = parseInt(form[pref.name].value, 10); if (isNaN(userValue)) { Morebits.Status.warn('Saving', 'The value you specified for ' + pref.name + ' (' + pref.value + ') was invalid. The save will continue, but the invalid data value will be skipped.'); userValue = null; } break; case 'set': // read from the set of check boxes userValue = []; if (pref.setDisplayOrder) { // read only those keys specified in the display order $.each(pref.setDisplayOrder, (itemkey, item) => { if (form[pref.name + '_' + item].checked) { userValue.push(item); } }); } else { // read all the keys in the list of values $.each(pref.setValues, (itemkey) => { if (form[pref.name + '_' + itemkey].checked) { userValue.push(itemkey); } }); } break; case 'customList': // read from the jQuery data stored on the button object userValue = $(form[pref.name]).data('value'); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } } else if (Twinkle.prefs) { // Retain the hidden preferences that may have customised by the user from twinkleoptions.js // undefined if not set userValue = Twinkle.prefs[pref.name]; } } // only save those preferences that are *different* from the default if (userValue !== undefined && !compare(userValue, Twinkle.defaultConfig[pref.name])) { newConfig[pref.name] = userValue; } }); }); let text = '// twinkleoptions.js: personal Twinkle preferences file\n' + '//\n' + '// NOTE: The easiest way to change your Twinkle preferences is by using the\n' + '// Twinkle preferences panel, at [[' + Morebits.pageNameNorm + ']].\n' + '//\n' + '// This file is AUTOMATICALLY GENERATED. Any changes you make (aside from\n' + '// changing the configuration parameters in a valid-JavaScript way) will be\n' + '// overwritten the next time you click "save" in the Twinkle preferences\n' + '// panel. If modifying this file, make sure to use correct JavaScript.\n' + // eslint-disable-next-line no-useless-concat '// <no' + 'wiki>\n' + '\n' + 'window.Twinkle.prefs = '; text += JSON.stringify(newConfig, null, 2); text += ';\n' + '\n' + // eslint-disable-next-line no-useless-concat '// </no' + 'wiki>\n' + '// End of twinkleoptions.js\n'; pageobj.setPageText(text); pageobj.setEditSummary('Saving Twinkle preferences: automatic edit from [[:' + Morebits.pageNameNorm + ']]'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('recreate'); pageobj.save(Twinkle.config.saveSuccess); }; Twinkle.config.saveSuccess = function twinkleconfigSaveSuccess(pageobj) { pageobj.getStatusElement().info('successful'); const noticebox = document.createElement('div'); noticebox.className = 'cdx-message cdx-message--success'; noticebox.style.fontSize = '100%'; noticebox.innerHTML = '<p><b>Your Twinkle preferences have been saved.</b> To see the changes, you will need to clear your browser cache entirely (see <a href="' + mw.util.getUrl('WP:BYPASS') + '" title="WP:BYPASS">WP:BYPASS</a> for instructions).</p>'; mw.loader.using('mediawiki.htmlform.codex.styles', () => { Morebits.Status.root.appendChild(noticebox); }); const noticeclear = document.createElement('br'); noticeclear.style.clear = 'both'; Morebits.Status.root.appendChild(noticeclear); }; Twinkle.addInitCallback(Twinkle.config.init); }()); // </nowiki> a3s7rfkqfx18l5b93fy769av25bi22y 365986 365985 2026-08-20T12:19:27Z ChiK 1136 ೧ revisions imported from [[:en:MediaWiki:Gadget-twinkleconfig.js]] 365985 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinkleconfig.js: Preferences module **************************************** * Mode of invocation: Adds configuration form to Wikipedia:Twinkle/Preferences, and adds an ad box to the top of user subpages belonging to the currently logged-in user which end in '.js' * Active on: What I just said. Yeah. */ Twinkle.config = {}; Twinkle.config.watchlistEnums = { yes: 'Add to watchlist (indefinitely)', no: "Don't add to watchlist", default: 'Follow your site preferences', '1 week': 'Watch for 1 week', '1 month': 'Watch for 1 month', '3 months': 'Watch for 3 months', '6 months': 'Watch for 6 months' }; Twinkle.config.commonSets = { csdCriteria: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', c4: 'C4', f1: 'F1', f2: 'F2', f3: 'F3', f7: 'F7', f8: 'F8', f9: 'F9', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5', g6: 'G6', g7: 'G7', g8: 'G8', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', t5: 'T5', u1: 'U1', u2: 'U2', u6: 'U6', u7: 'U7' }, csdCriteriaNotification: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', f1: 'F1', f2: 'F2', f3: 'F3', f7: 'F7', f9: 'F9', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5 ("general sanction violation" only)', g6: 'G6 ("copy-paste move" only)', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', u6: 'U6', u7: 'U7' }, csdAndImageDeletionCriteria: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', c4: 'C4', f1: 'F1', f2: 'F2', f3: 'F3', f4: 'F4', f5: 'F5', f6: 'F6', f7: 'F7', f8: 'F8', f9: 'F9', f11: 'F11', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5', g6: 'G6', g7: 'G7', g8: 'G8', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', t5: 'T5', u1: 'U1', u2: 'U2', u6: 'U6', u7: 'U7' }, namespacesNoSpecial: { 0: 'Article', 1: 'Talk (article)', 2: 'User', 3: 'User talk', 4: 'Wikipedia', 5: 'Wikipedia talk', 6: 'File', 7: 'File talk', 8: 'MediaWiki', 9: 'MediaWiki talk', 10: 'Template', 11: 'Template talk', 12: 'Help', 13: 'Help talk', 14: 'Category', 15: 'Category talk', 100: 'Portal', 101: 'Portal talk', 118: 'Draft', 119: 'Draft talk', 710: 'TimedText', 711: 'TimedText talk', 828: 'Module', 829: 'Module talk' } }; Twinkle.config.commonSets.csdCriteriaDisplayOrder = Object.keys(Twinkle.config.commonSets.csdCriteria); Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder = Object.keys(Twinkle.config.commonSets.csdCriteriaNotification); Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder = Object.keys(Twinkle.config.commonSets.csdAndImageDeletionCriteria); /** * Section entry format: * * { * title: <human-readable section title>, * module: <name of the associated module, used to link to sections>, * adminOnly: <true for admin-only sections>, * hidden: <true for advanced preferences that rarely need to be changed - they can still be modified by manually editing twinkleoptions.js>, * preferences: [ * { * name: <TwinkleConfig property name>, * label: <human-readable short description - used as a form label>, * helptip: <(optional) human-readable text (using valid HTML) that complements the description, like limits, warnings, etc.> * adminOnly: <true for admin-only preferences>, * type: <string|boolean|integer|enum|set|customList> (customList stores an array of JSON objects { value, label }), * enumValues: <for type = "enum": a JSON object where the keys are the internal names and the values are human-readable strings>, * setValues: <for type = "set": a JSON object where the keys are the internal names and the values are human-readable strings>, * setDisplayOrder: <(optional) for type = "set": an array containing the keys of setValues (as strings) in the order that they are displayed>, * customListValueTitle: <for type = "customList": the heading for the left "value" column in the custom list editor>, * customListLabelTitle: <for type = "customList": the heading for the right "label" column in the custom list editor> * }, * . . . * ] * }, * . . . * */ Twinkle.config.sections = [ { title: 'General', module: 'general', preferences: [ // TwinkleConfig.userTalkPageMode may take arguments: // 'window': open a new window, remember the opened window // 'tab': opens in a new tab, if possible. // 'blank': force open in a new window, even if such a window exists { name: 'userTalkPageMode', label: 'When opening a user talk page, open it', type: 'enum', enumValues: { window: 'In a window, replacing other user talks', tab: 'In a new tab', blank: 'In a totally new window' } }, // TwinkleConfig.dialogLargeFont (boolean) { name: 'dialogLargeFont', label: 'Use larger text in Twinkle dialogs', type: 'boolean' }, // Twinkle.config.disabledModules (array) { name: 'disabledModules', label: 'Turn off the selected Twinkle modules', helptip: 'Anything you select here will NOT be available for use, so act with care. Uncheck to reactivate.', type: 'set', setValues: { arv: 'ARV', warn: 'Warn', welcome: 'Welcome', talkback: 'Talkback', speedy: 'CSD', prod: 'PROD', xfd: 'XfD', image: 'Image (DI)', protect: 'Protect (RPP)', tag: 'Tag', diff: 'Diff', unlink: 'Unlink', rollback: 'Revert and rollback' } }, // Twinkle.config.disabledSysopModules (array) { name: 'disabledSysopModules', label: 'Turn off the selected admin-only modules', helptip: 'Anything you select here will NOT be available for use, so act with care. Uncheck to reactivate.', adminOnly: true, type: 'set', setValues: { block: 'Block', deprod: 'DePROD', batchdelete: 'D-batch', batchprotect: 'P-batch', batchundelete: 'Und-batch' } } ] }, { title: 'ARV', module: 'arv', preferences: [ { name: 'spiWatchReport', label: 'Add sockpuppet report pages to watchlist', type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Block user', module: 'block', adminOnly: true, preferences: [ // TwinkleConfig.defaultToBlock64 (boolean) // Whether to default to just blocking the /64 on or off { name: 'defaultToBlock64', label: 'For IPv6 addresses, select the option to block the /64 range by default', type: 'boolean' }, // TwinkleConfig.defaultToPartialBlocks (boolean) // Whether to default partial blocks on or off { name: 'defaultToPartialBlocks', label: 'Select partial blocks by default when opening the block menu', helptip: 'If the user is already blocked, this will be overridden in favor of defaulting to the current block type', type: 'boolean' }, // TwinkleConfig.blankTalkpageOnIndefBlock (boolean) // if true, blank the talk page when issuing an indef block notice (per [[WP:UWUL#Indefinitely blocked users]]) { name: 'blankTalkpageOnIndefBlock', label: 'Blank the talk page when indefinitely blocking users', helptip: 'See <a href="' + mw.util.getUrl('Wikipedia:WikiProject_User_warnings/Usage_and_layout#Indefinitely_blocked_users') + '">WP:UWUL</a> for more information.', type: 'boolean' } ] }, { title: 'Image deletion (DI)', module: 'image', preferences: [ // TwinkleConfig.notifyUserOnDeli (boolean) // If the user should be notified after placing a file deletion tag { name: 'notifyUserOnDeli', label: 'Check the "notify initial uploader" box by default', type: 'boolean' }, // TwinkleConfig.deliWatchPage (string) // The watchlist setting of the page tagged for deletion. { name: 'deliWatchPage', label: 'Add image page to watchlist when tagging', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.deliWatchUser (string) // The watchlist setting of the user talk page if a notification is placed. { name: 'deliWatchUser', label: 'Add user talk page of initial uploader to watchlist when notifying', type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Page protection ' + (Morebits.userIsSysop ? '(PP)' : '(RPP)'), module: 'protect', preferences: [ { name: 'watchRequestedPages', label: 'Add page to watchlist when requesting protection', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchPPTaggedPages', label: 'Add page to watchlist when tagging with protection template', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchProtectedPages', label: 'Add page to watchlist when protecting', helptip: 'If also tagging the page after protection, that preference will be favored.', adminOnly: true, type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Proposed deletion (PROD)', module: 'prod', preferences: [ // TwinkleConfig.watchProdPages (string) // Watchlist setting when applying prod template to page { name: 'watchProdPages', label: 'Add article to watchlist when tagging', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.markProdPagesAsPatrolled (boolean) // If, when applying prod template to page, to mark the page as curated/patrolled (if the page was reached from NewPages) { name: 'markProdPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when tagging (if possible)', helptip: 'This should probably not be checked as doing so is against best practice consensus', type: 'boolean' }, // TwinkleConfig.prodReasonDefault (string) // The prefilled PROD reason. { name: 'prodReasonDefault', label: 'Prefilled PROD reason', type: 'string' }, { name: 'logProdPages', label: 'Keep a log in userspace of all pages you tag for PROD', helptip: 'Since non-admins do not have access to their deleted contributions, the userspace log offers a good way to keep track of all pages you tag for PROD using Twinkle.', type: 'boolean' }, { name: 'prodLogPageName', label: 'Keep the PROD userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your PROD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the PROD userspace log.', type: 'string' } ] }, { title: 'Revert and rollback', module: 'rollback', preferences: [ // TwinkleConfig.autoMenuAfterRollback (bool) // Option to automatically open the warning menu if the user talk page is opened post-reversion { name: 'autoMenuAfterRollback', label: 'Automatically open the Twinkle warn menu on a user talk page after Twinkle rollback', helptip: 'Only operates if the relevant box is checked below.', type: 'boolean' }, // TwinkleConfig.openTalkPage (array) // What types of actions that should result in opening of talk page { name: 'openTalkPage', label: 'Open user talk page after these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback' } }, // TwinkleConfig.openTalkPageOnAutoRevert (bool) // Defines if talk page should be opened when calling revert from contribs or recent changes pages. If set to true, openTalkPage defines then if talk page will be opened. { name: 'openTalkPageOnAutoRevert', label: 'Open user talk page when invoking rollback from user contributions or recent changes', helptip: 'When this is on, the desired options must be enabled in the previous setting for this to work.', type: 'boolean' }, // TwinkleConfig.rollbackInPlace (bool) // { name: 'rollbackInPlace', label: "Don't reload the page when rolling back from contributions or recent changes", helptip: "When this is on, Twinkle won't reload the contributions or recent changes feed after reverting, allowing you to revert more than one edit at a time.", type: 'boolean' }, // TwinkleConfig.markRevertedPagesAsMinor (array) // What types of actions that should result in marking edit as minor { name: 'markRevertedPagesAsMinor', label: 'Mark as minor edit for these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback', torev: '"Restore this version"' } }, // TwinkleConfig.watchRevertedPages (array) // What types of actions that should result in forced addition to watchlist { name: 'watchRevertedPages', label: 'Add pages to watchlist for these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback', torev: '"Restore this version"' } }, // TwinkleConfig.watchRevertedExpiry // If any of the above items are selected, whether to expire the watch { name: 'watchRevertedExpiry', label: 'When reverting a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.offerReasonOnNormalRevert (boolean) // If to offer a prompt for extra summary reason for normal reverts, default to true { name: 'offerReasonOnNormalRevert', label: 'Prompt for reason for normal rollbacks', helptip: '"Normal" rollbacks are the ones that are invoked from the middle [rollback] link.', type: 'boolean' }, { name: 'confirmOnRollback', label: 'Require confirmation before reverting (all devices)', helptip: 'For users of pen or touch devices, and chronically indecisive people.', type: 'boolean' }, { name: 'confirmOnMobileRollback', label: 'Require confirmation before reverting (mobile devices only)', helptip: 'Avoid accidental reversions when on mobile devices.', type: 'boolean' }, // TwinkleConfig.showRollbackLinks (array) // Where Twinkle should show rollback links: // diff, others, mine, contribs, history, recent // Note from TTO: |contribs| seems to be equal to |others| + |mine|, i.e. redundant, so I left it out heres { name: 'showRollbackLinks', label: 'Show rollback links on these pages', type: 'set', setValues: { diff: 'Diff pages', others: 'Contributions pages of other users', mine: 'My contributions page', recent: 'Recent changes and related changes special pages', history: 'History pages' } } ] }, { title: 'Speedy deletion (CSD)', module: 'speedy', preferences: [ { name: 'speedySelectionStyle', label: 'When to go ahead and tag/delete the page', type: 'enum', enumValues: { buttonClick: 'When I click "Submit"', radioClick: 'As soon as I click an option' } }, // TwinkleConfig.watchSpeedyPages (array) // Whether to add speedy tagged or deleted pages to watchlist { name: 'watchSpeedyPages', label: 'Add page to watchlist when using these criteria', type: 'set', setValues: Twinkle.config.commonSets.csdCriteria, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder }, // TwinkleConfig.watchSpeedyExpiry // If any of the above items are selected, whether to expire the watch { name: 'watchSpeedyExpiry', label: 'When tagging a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.markSpeedyPagesAsPatrolled (boolean) // If, when applying speedy template to page, to mark the page as triaged/patrolled (if the page was reached from NewPages) { name: 'markSpeedyPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when tagging (if possible)', helptip: 'This should probably not be checked as doing so is against best practice consensus', type: 'boolean' }, // TwinkleConfig.watchSpeedyUser (string) // The watchlist setting of the user talk page if they receive a notification. { name: 'watchSpeedyUser', label: 'Add user talk page of initial contributor to watchlist (when notifying)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.welcomeUserOnSpeedyDeletionNotification (array of strings) // On what types of speedy deletion notifications shall the user be welcomed // with a "firstarticle" notice if their talk page has not yet been created. { name: 'welcomeUserOnSpeedyDeletionNotification', label: 'Welcome page creator when notifying with these criteria', helptip: 'The welcome is issued only if the user is notified about the deletion, and only if their talk page does not already exist. The template used is {{firstarticle}}.', type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.notifyUserOnSpeedyDeletionNomination (array) // What types of actions should result in the author of the page being notified of nomination { name: 'notifyUserOnSpeedyDeletionNomination', label: 'Notify page creator when tagging with these criteria', helptip: 'Even if you choose to notify from the CSD screen, the notification will only take place for those criteria selected here.', type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.warnUserOnSpeedyDelete (array) // What types of actions should result in the author of the page being notified of speedy deletion (admin only) { name: 'warnUserOnSpeedyDelete', label: 'Notify page creator when deleting under these criteria', helptip: 'Even if you choose to notify from the CSD screen, the notification will only take place for those criteria selected here.', adminOnly: true, type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.promptForSpeedyDeletionSummary (array of strings) { name: 'promptForSpeedyDeletionSummary', label: 'Allow editing of deletion summary when deleting under these criteria', adminOnly: true, type: 'set', setValues: Twinkle.config.commonSets.csdAndImageDeletionCriteria, setDisplayOrder: Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder }, // TwinkleConfig.deleteTalkPageOnDelete (boolean) // If talk page if exists should also be deleted (CSD G8) when spedying a page (admin only) { name: 'deleteTalkPageOnDelete', label: 'Check the "also delete talk page" box by default', adminOnly: true, type: 'boolean' }, { name: 'deleteRedirectsOnDelete', label: 'Check the "also delete redirects" box by default', adminOnly: true, type: 'boolean' }, // TwinkleConfig.deleteSysopDefaultToDelete (boolean) // Make the CSD screen default to "delete" instead of "tag" (admin only) { name: 'deleteSysopDefaultToDelete', label: 'Default to outright deletion instead of speedy tagging', helptip: 'If there is a CSD tag already present, Twinkle will always default to "delete" mode', adminOnly: true, type: 'boolean' }, // TwinkleConfig.speedyWindowWidth (integer) // Defines the width of the Twinkle SD window in pixels { name: 'speedyWindowWidth', label: 'Width of speedy deletion window (pixels)', type: 'integer' }, // TwinkleConfig.speedyWindowWidth (integer) // Defines the width of the Twinkle SD window in pixels { name: 'speedyWindowHeight', label: 'Height of speedy deletion window (pixels)', helptip: 'If you have a big monitor, you might like to increase this.', type: 'integer' }, { name: 'logSpeedyNominations', label: 'Keep a log in userspace of all CSD nominations', helptip: 'Since non-admins do not have access to their deleted contributions, the userspace log offers a good way to keep track of all pages you nominate for CSD using Twinkle. Files tagged using DI are also added to this log.', type: 'boolean' }, { name: 'speedyLogPageName', label: 'Keep the CSD userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your CSD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the CSD userspace log.', type: 'string' }, { name: 'noLogOnSpeedyNomination', label: 'Do not create a userspace log entry when tagging with these criteria', type: 'set', setValues: Twinkle.config.commonSets.csdAndImageDeletionCriteria, setDisplayOrder: Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder } ] }, { title: 'Tag', module: 'tag', preferences: [ { name: 'watchTaggedVenues', label: 'Add page to watchlist when tagging these type of pages', type: 'set', setValues: { articles: 'Articles', drafts: 'Drafts', redirects: 'Redirects', files: 'Files' } }, { name: 'watchTaggedPages', label: 'When tagging a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchMergeDiscussions', label: 'Add talk pages to watchlist when starting merge discussions', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'markTaggedPagesAsMinor', label: 'Mark addition of tags as a minor edit', type: 'boolean' }, { name: 'markTaggedPagesAsPatrolled', label: 'Check the "mark page as patrolled/reviewed" box by default', type: 'boolean' }, { name: 'groupByDefault', label: 'Check the "group into {{multiple issues}}" box by default', type: 'boolean' }, { name: 'tagArticleSortOrder', label: 'Default view order for article tags', type: 'enum', enumValues: { cat: 'By categories', alpha: 'In alphabetical order' } }, { name: 'customTagList', label: 'Custom article/draft maintenance tags to display', helptip: "These appear as additional options at the bottom of the list of tags. For example, you could add new maintenance tags which have not yet been added to Twinkle's defaults.", type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' }, { name: 'customFileTagList', label: 'Custom file maintenance tags to display', helptip: 'Additional tags that you wish to add for files.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' }, { name: 'customRedirectTagList', label: 'Custom redirect category tags to display', helptip: 'Additional tags that you wish to add for redirects.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' } ] }, { title: 'Talkback', module: 'talkback', preferences: [ { name: 'markTalkbackAsMinor', label: 'Mark talkbacks as minor edits', type: 'boolean' }, { name: 'insertTalkbackSignature', label: 'Insert signature within talkbacks', type: 'boolean' }, { name: 'talkbackHeading', label: 'Section heading to use for talkback and please see', tooltip: 'Should NOT include the equals signs ("==") used for wikitext formatting', type: 'string' }, { name: 'mailHeading', label: "Section heading to use for \"you've got mail\" notices", tooltip: 'Should NOT include the equals signs ("==") used for wikitext formatting', type: 'string' } ] }, { title: 'Unlink', module: 'unlink', preferences: [ // TwinkleConfig.unlinkNamespaces (array) // In what namespaces unlink should happen, default in 0 (article), 10 (template), 100 (portal), and 118 (draft) { name: 'unlinkNamespaces', label: 'Remove links from pages in these namespaces', helptip: 'Avoid selecting any talk namespaces, as Twinkle might end up unlinking on talk archives (a big no-no).', type: 'set', setValues: Twinkle.config.commonSets.namespacesNoSpecial } ] }, { title: 'Warn user', module: 'warn', preferences: [ // TwinkleConfig.defaultWarningGroup (int) // Which level warning should be the default selected group, default is 1 { name: 'defaultWarningGroup', label: 'Default warning level', type: 'enum', enumValues: { 1: 'Level 1', 2: 'Level 2', 3: 'Level 3', 4: 'Level 4', 5: 'Level 4im', 6: 'Single-issue notices', 7: 'Single-issue warnings', // 8 was used for block templates before #260 9: 'Custom warnings', 10: 'All warning templates', 11: 'Auto-select level (1-4)' } }, // TwinkleConfig.combinedSingletMenus (boolean) // if true, show one menu with both single-issue notices and warnings instead of two separately { name: 'combinedSingletMenus', label: 'Replace the two separate single-issue menus into one combined menu', helptip: 'Selecting either single-issue notices or single-issue warnings as your default will make this your default if enabled.', type: 'boolean' }, // TwinkleConfig.watchWarnings (string) // Watchlist setting for the page which has been dispatched an warning or notice { name: 'watchWarnings', label: 'Add user talk page to watchlist when notifying', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.oldSelect (boolean) // if true, use the native select menu rather the select2-based one { name: 'oldSelect', label: 'Use the non-searchable classic select menu', type: 'boolean' }, { name: 'customWarningList', label: 'Custom warning templates to display', helptip: 'You can add individual templates or user subpages. Custom warnings appear in the "Custom warnings" category within the warning dialog box.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in warning list (also used as edit summary)' } ] }, { title: 'Welcome user', module: 'welcome', preferences: [ { name: 'topWelcomes', label: 'Place welcomes above existing content on user talk pages', type: 'boolean' }, { name: 'watchWelcomes', label: 'Add user talk pages to watchlist when welcoming', helptip: 'Doing so adds to the personal element of welcoming a user - you will be able to see how they are coping as a newbie, and possibly help them.', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'insertUsername', label: 'Add your username to the template (where applicable)', helptip: "Some welcome templates have an opening sentence like \"Hi, I'm &lt;username&gt;. Welcome\" etc. If you turn off this option, these templates will not display your username in that way.", type: 'boolean' }, { name: 'quickWelcomeMode', label: 'Clicking the "welcome" link on a diff page (which only appears if the editor\'s user talk page has not been created yet) will', helptip: 'If you choose to welcome automatically, the template you specify below will be used.', type: 'enum', enumValues: { auto: 'immediately post the welcome template specified below', norm: 'prompt you to select a template' } }, { name: 'quickWelcomeTemplate', label: 'Template to use when welcoming automatically', helptip: 'Enter the name of a welcome template, without the curly brackets. A link to the given article will be added.', type: 'string' }, { name: 'customWelcomeList', label: 'Custom welcome templates to display', helptip: "You can add other welcome templates, or user subpages that are welcome templates (prefixed with \"User:\"). Don't forget that these templates are substituted onto user talk pages.", type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Welcome dialog' }, { name: 'customWelcomeSignature', label: 'Automatically sign custom welcome templates', helptip: 'If your custom welcome templates contain a built-in signature within the template, turn off this option.', type: 'boolean' } ] }, { title: 'XFD (deletion discussions)', module: 'xfd', preferences: [ { name: 'logXfdNominations', label: 'Keep a log in userspace of all pages you nominate for a deletion discussion (XfD)', helptip: 'The userspace log offers a good way to keep track of all pages you nominate for XfD using Twinkle.', type: 'boolean' }, { name: 'xfdLogPageName', label: 'Keep the deletion discussion userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your XfD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the XfD userspace log.', type: 'string' }, { name: 'noLogOnXfdNomination', label: 'Do not create a userspace log entry when nominating at this venue', type: 'set', setValues: { afd: 'AfD', tfd: 'TfD', ffd: 'FfD', cfd: 'CfD', cfds: 'CfD/S', mfd: 'MfD', rfd: 'RfD', rm: 'RM' } }, // TwinkleConfig.xfdWatchPage (string) // The watchlist setting of the page being nominated for XfD. { name: 'xfdWatchPage', label: 'Add the nominated page to watchlist', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchDiscussion (string) // The watchlist setting of the newly created XfD page (for those processes that create discussion pages for each nomination), // or the list page for the other processes. { name: 'xfdWatchDiscussion', label: 'Add the deletion discussion page to watchlist', helptip: 'This refers to the discussion subpage (for AfD and MfD) or the daily log page (for TfD, CfD, RfD and FfD)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchList (string) // The watchlist setting of the XfD list page, *if* the discussion is on a separate page. { name: 'xfdWatchList', label: 'Add the daily log/list page to the watchlist (AfD and MfD)', helptip: 'This only applies for AfD and MfD, where the discussions are transcluded onto a daily log page (for AfD) or the main MfD page (for MfD).', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchUser (string) // The watchlist setting of the user talk page if they receive a notification. { name: 'xfdWatchUser', label: 'Add user talk page of initial contributor to watchlist (when notifying)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchRelated (string) // The watchlist setting of the target of a redirect being nominated for RfD. { name: 'xfdWatchRelated', label: "Add the redirect's target page to watchlist (when notifying)", helptip: 'This only applies for RfD, when leaving a notification on the talk page of the target of the redirect', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'markXfdPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when nominating for AFD (if possible)', type: 'boolean' } ] }, { title: 'Hidden', hidden: true, preferences: [ // twinklerollback.js: defines how many revision to query maximum, maximum possible is 50, default is 50 { name: 'revertMaxRevisions', type: 'integer' }, // twinklewarn.js: When using the autolevel select option, how many days makes a prior warning stale // Huggle is three days ([[Special:Diff/918980316]] and [[Special:Diff/919417999]]) while ClueBotNG is two: // https://github.com/DamianZaremba/cluebotng/blob/4958e25d6874cba01c75f11debd2e511fd5a2ce5/bot/action_functions.php#L62 { name: 'autolevelStaleDays', type: 'integer' }, // How many pages should be queried by deprod and batchdelete/protect/undelete { name: 'batchMax', type: 'integer', adminOnly: true }, // How many pages should be processed at a time by deprod and batchdelete/protect/undelete { name: 'batchChunks', type: 'integer', adminOnly: true } ] } ]; // end of Twinkle.config.sections Twinkle.config.init = function twinkleconfigInit() { // create the config page at Wikipedia:Twinkle/Preferences if ((mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').project && mw.config.get('wgTitle') === 'Twinkle/Preferences') && mw.config.get('wgAction') === 'view') { if (!document.getElementById('twinkle-config')) { return; // maybe the page is misconfigured, or something - but any attempt to modify it will be pointless } // set style to nothing to prevent conflict with external css document.getElementById('twinkle-config').removeAttribute('style'); document.getElementById('twinkle-config-titlebar').removeAttribute('style'); const contentdiv = document.getElementById('twinkle-config-content'); contentdiv.textContent = ''; // clear children // let user know about possible conflict with skin js/common.js file // (settings in that file will still work, but they will be overwritten by twinkleoptions.js settings) if (window.TwinkleConfig || window.FriendlyConfig) { const contentnotice = document.createElement('div'); contentnotice.className = 'plainlinks twinkle-ombox'; contentnotice.innerHTML = '<div>' + '<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/3/38/Imbox_content.png" />' + '</div>' + '<div>' + '<p><big><b>Before modifying your settings here,</b> you must remove your old Twinkle and Friendly settings from your personal skin JavaScript.</big></p>' + '<p>To do this, you can <a href="' + mw.util.getUrl('User:' + mw.config.get('wgUserName') + '/' + mw.config.get('skin') + '.js', { action: 'edit' }) + '" target="_blank"><b>edit your personal skin javascript file</b></a> or <a href="' + mw.util.getUrl('User:' + mw.config.get('wgUserName') + '/common.js', { action: 'edit'}) + '" target="_blank"><b>your common.js file</b></a>, removing all lines of code that refer to <code>TwinkleConfig</code> and <code>FriendlyConfig</code>.</p>' + '</div>'; contentdiv.appendChild(contentnotice); } // start a table of contents const toctable = document.createElement('div'); toctable.className = 'toc'; toctable.style.marginLeft = '0.4em'; // create TOC title const toctitle = document.createElement('div'); toctitle.id = 'toctitle'; const toch2 = document.createElement('h2'); toch2.textContent = 'Contents '; toctitle.appendChild(toch2); // add TOC show/hide link const toctoggle = document.createElement('span'); toctoggle.className = 'toctoggle'; toctoggle.appendChild(document.createTextNode('[')); const toctogglelink = document.createElement('a'); toctogglelink.className = 'internal'; toctogglelink.setAttribute('href', '#tw-tocshowhide'); toctogglelink.textContent = 'hide'; toctoggle.appendChild(toctogglelink); toctoggle.appendChild(document.createTextNode(']')); toctitle.appendChild(toctoggle); toctable.appendChild(toctitle); // create item container: this is what we add stuff to const tocul = document.createElement('ul'); toctogglelink.addEventListener('click', () => { const $tocul = $(tocul); $tocul.toggle(); if ($tocul.find(':visible').length) { toctogglelink.textContent = 'hide'; } else { toctogglelink.textContent = 'show'; } }, false); toctable.appendChild(tocul); contentdiv.appendChild(toctable); const contentform = document.createElement('form'); contentform.setAttribute('action', 'javascript:void(0)'); // was #tw-save - changed to void(0) to work around Chrome issue contentform.addEventListener('submit', Twinkle.config.save, true); contentdiv.appendChild(contentform); const container = document.createElement('table'); container.style.width = '100%'; contentform.appendChild(container); $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // i.e. "continue" in this context } // add to TOC const tocli = document.createElement('li'); tocli.className = 'toclevel-1'; const toca = document.createElement('a'); toca.setAttribute('href', '#' + section.module); toca.appendChild(document.createTextNode(section.title)); tocli.appendChild(toca); tocul.appendChild(tocli); let row = document.createElement('tr'); let cell = document.createElement('td'); cell.setAttribute('colspan', '3'); const heading = document.createElement('h4'); heading.style.borderBottom = '1px solid gray'; heading.style.marginTop = '0.2em'; heading.id = section.module; heading.appendChild(document.createTextNode(section.title)); cell.appendChild(heading); row.appendChild(cell); container.appendChild(row); let rowcount = 1; // for row banding // add each of the preferences to the form $(section.preferences).each((prefkey, pref) => { if (pref.adminOnly && !Morebits.userIsSysop) { return true; // i.e. "continue" in this context } row = document.createElement('tr'); row.style.marginBottom = '0.2em'; // create odd row banding if (rowcount++ % 2 === 0) { row.style.backgroundColor = 'rgba(128, 128, 128, 0.1)'; } cell = document.createElement('td'); let label, input; const gotPref = Twinkle.getPref(pref.name); switch (pref.type) { case 'boolean': // create a checkbox cell.setAttribute('colspan', '2'); label = document.createElement('label'); input = document.createElement('input'); input.setAttribute('type', 'checkbox'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); if (gotPref === true) { input.setAttribute('checked', 'checked'); } label.appendChild(input); label.appendChild(document.createTextNode(pref.label)); cell.appendChild(label); break; case 'string': // create an input box case 'integer': // add label to first column cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add input box to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; input = document.createElement('input'); input.setAttribute('type', 'text'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); if (pref.type === 'integer') { input.setAttribute('size', 6); input.setAttribute('type', 'number'); input.setAttribute('step', '1'); // integers only } if (gotPref) { input.setAttribute('value', gotPref); } cell.appendChild(input); break; case 'enum': // create a combo box // add label to first column // note: duplicates the code above, under string/integer cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add input box to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; input = document.createElement('select'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); $.each(pref.enumValues, (enumvalue, enumdisplay) => { const option = document.createElement('option'); option.setAttribute('value', enumvalue); if ((gotPref === enumvalue) || // Hack to convert old boolean watchlist prefs // to corresponding enums (added in v2.1) (typeof gotPref === 'boolean' && ((gotPref && enumvalue === 'yes') || (!gotPref && enumvalue === 'no')))) { option.setAttribute('selected', 'selected'); } option.appendChild(document.createTextNode(enumdisplay)); input.appendChild(option); }); cell.appendChild(input); break; case 'set': // create a set of check boxes // add label first of all cell.setAttribute('colspan', '2'); label = document.createElement('label'); // not really necessary to use a label element here, but we do it for consistency of styling label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); var checkdiv = document.createElement('div'); checkdiv.style.paddingLeft = '1em'; var worker = function(itemkey, itemvalue) { const checklabel = document.createElement('label'); checklabel.style.marginRight = '0.7em'; checklabel.style.display = 'inline-block'; const check = document.createElement('input'); check.setAttribute('type', 'checkbox'); check.setAttribute('id', pref.name + '_' + itemkey); check.setAttribute('name', pref.name + '_' + itemkey); if (gotPref && gotPref.includes(itemkey)) { check.setAttribute('checked', 'checked'); } // cater for legacy integer array values for unlinkNamespaces (this can be removed a few years down the track...) if (pref.name === 'unlinkNamespaces') { if (gotPref && gotPref.includes(parseInt(itemkey, 10))) { check.setAttribute('checked', 'checked'); } } checklabel.appendChild(check); checklabel.appendChild(document.createTextNode(itemvalue)); checkdiv.appendChild(checklabel); }; if (pref.setDisplayOrder) { // add check boxes according to the given display order $.each(pref.setDisplayOrder, (itemkey, item) => { worker(item, pref.setValues[item]); }); } else { // add check boxes according to the order it gets fed to us (probably strict alphabetical) $.each(pref.setValues, worker); } cell.appendChild(checkdiv); break; case 'customList': // add label to first column cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add button to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; var button = document.createElement('button'); button.setAttribute('id', pref.name); button.setAttribute('name', pref.name); button.setAttribute('type', 'button'); button.addEventListener('click', Twinkle.config.listDialog.display, false); // use jQuery data on the button to store the current config value $(button).data({ value: gotPref, pref: pref }); button.appendChild(document.createTextNode('Edit items')); cell.appendChild(button); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } row.appendChild(cell); // add help tip cell = document.createElement('td'); cell.className = 'twinkle-config-helptip'; if (pref.helptip) { // convert mentions of templates in the helptip to clickable links cell.innerHTML = pref.helptip.replace(/{{(.+?)}}/g, '{{<a href="' + mw.util.getUrl('Template:') + '$1" target="_blank">$1</a>}}'); } // add reset link (custom lists don't need this, as their config value isn't displayed on the form) if (pref.type !== 'customList') { const resetlink = document.createElement('a'); resetlink.setAttribute('href', '#tw-reset'); resetlink.setAttribute('id', 'twinkle-config-reset-' + pref.name); resetlink.addEventListener('click', Twinkle.config.resetPrefLink, false); resetlink.style.cssFloat = 'right'; resetlink.style.margin = '0 0.6em'; resetlink.appendChild(document.createTextNode('Reset')); cell.appendChild(resetlink); } row.appendChild(cell); container.appendChild(row); return true; }); return true; }); const footerbox = document.createElement('div'); footerbox.setAttribute('id', 'twinkle-config-buttonpane'); const button = document.createElement('button'); button.setAttribute('id', 'twinkle-config-submit'); button.setAttribute('type', 'submit'); button.appendChild(document.createTextNode('Save changes')); footerbox.appendChild(button); const footerspan = document.createElement('span'); footerspan.className = 'plainlinks'; footerspan.style.marginLeft = '2.4em'; footerspan.style.fontSize = '90%'; const footera = document.createElement('a'); footera.setAttribute('href', '#tw-reset-all'); footera.setAttribute('id', 'twinkle-config-resetall'); footera.addEventListener('click', Twinkle.config.resetAllPrefs, false); footera.appendChild(document.createTextNode('Restore defaults')); footerspan.appendChild(footera); footerbox.appendChild(footerspan); contentform.appendChild(footerbox); // since all the section headers exist now, we can try going to the requested anchor if (window.location.hash) { const loc = window.location.hash; window.location.hash = ''; window.location.hash = loc; } } else if (mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').user && mw.config.get('wgTitle').indexOf(mw.config.get('wgUserName')) === 0 && mw.config.get('wgPageName').slice(-3) === '.js') { const box = document.createElement('div'); // Styled in twinkle.css box.setAttribute('id', 'twinkle-config-headerbox'); let link; const scriptPageName = mw.config.get('wgPageName').slice( mw.config.get('wgPageName').lastIndexOf('/') + 1, mw.config.get('wgPageName').lastIndexOf('.js') ); if (scriptPageName === 'twinkleoptions') { // place "why not try the preference panel" notice box.setAttribute('class', 'config-twopt-box'); if (mw.config.get('wgArticleId') > 0) { // page exists box.appendChild(document.createTextNode('This page contains your Twinkle preferences. You can change them using the ')); } else { // page does not exist box.appendChild(document.createTextNode('You can customize Twinkle to suit your preferences by using the ')); } link = document.createElement('a'); link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':Twinkle/Preferences')); link.appendChild(document.createTextNode('Twinkle preferences panel')); box.appendChild(link); box.appendChild(document.createTextNode(', or by editing this page.')); $(box).insertAfter($('#contentSub')); } else if (['monobook', 'vector', 'vector-2022', 'cologneblue', 'modern', 'timeless', 'minerva', 'common'].includes(scriptPageName)) { // place "Looking for Twinkle options?" notice box.setAttribute('class', 'config-userskin-box'); box.appendChild(document.createTextNode('If you want to set Twinkle preferences, you can use the ')); link = document.createElement('a'); link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':Twinkle/Preferences')); link.appendChild(document.createTextNode('Twinkle preferences panel')); box.appendChild(link); box.appendChild(document.createTextNode('.')); $(box).insertAfter($('#contentSub')); } } }; // custom list-related stuff Twinkle.config.listDialog = {}; Twinkle.config.listDialog.addRow = function twinkleconfigListDialogAddRow($dlgtable, value, label) { let $contenttr, $valueInput, $labelInput; $dlgtable.append( $contenttr = $('<tr>').append( $('<td>').append( $('<button>') .attr('type', 'button') .on('click', () => { $contenttr.remove(); }) .text('Remove') ), $('<td>').append( $valueInput = $('<input>') .attr('type', 'text') .addClass('twinkle-config-customlist-value') .css('width', '97%') ), $('<td>').append( $labelInput = $('<input>') .attr('type', 'text') .addClass('twinkle-config-customlist-label') .css('width', '98%') ) ) ); if (value) { $valueInput.val(value); } if (label) { $labelInput.val(label); } }; Twinkle.config.listDialog.display = function twinkleconfigListDialogDisplay(e) { const $prefbutton = $(e.target); const curvalue = $prefbutton.data('value'); const curpref = $prefbutton.data('pref'); const dialog = new Morebits.SimpleWindow(720, 400); dialog.setTitle(curpref.label); dialog.setScriptName('Twinkle preferences'); let $dlgtbody; dialog.setContent( $('<div>').append( $('<table>') .addClass('wikitable') .css({ margin: '1.4em 1em', width: 'auto' }) .append( $dlgtbody = $('<tbody>').append( // header row $('<tr>').append( $('<th>') // top-left cell .css('width', '5%'), $('<th>') // value column header .css('width', '35%') .text(curpref.customListValueTitle ? curpref.customListValueTitle : 'Value'), $('<th>') // label column header .css('width', '60%') .text(curpref.customListLabelTitle ? curpref.customListLabelTitle : 'Label') ) ), $('<tfoot>').append( $('<tr>').append( $('<td>') .attr('colspan', '3') .append( $('<button>') .text('Add') .css('min-width', '8em') .attr('type', 'button') .on('click', () => { Twinkle.config.listDialog.addRow($dlgtbody); }) ) ) ) ), $('<button>') .text('Save changes') .attr('type', 'submit') // so Morebits.SimpleWindow puts the button in the button pane .on('click', () => { Twinkle.config.listDialog.save($prefbutton, $dlgtbody); dialog.close(); }), $('<button>') .text('Reset') .attr('type', 'submit') .on('click', () => { Twinkle.config.listDialog.reset($prefbutton, $dlgtbody); }), $('<button>') .text('Cancel') .attr('type', 'submit') .on('click', () => { dialog.close(); }) )[0] ); // content rows let gotRow = false; $.each(curvalue, (k, v) => { gotRow = true; Twinkle.config.listDialog.addRow($dlgtbody, v.value, v.label); }); // if there are no values present, add a blank row to start the user off if (!gotRow) { Twinkle.config.listDialog.addRow($dlgtbody); } dialog.display(); }; // Resets the data value, re-populates based on the new (default) value, then saves the // old data value again (less surprising behaviour) Twinkle.config.listDialog.reset = function twinkleconfigListDialogReset($button, $tbody) { // reset value on button const curpref = $button.data('pref'); const oldvalue = $button.data('value'); Twinkle.config.resetPref(curpref); // reset form $tbody.find('tr').slice(1).remove(); // all rows except the first (header) row // add the new values const curvalue = $button.data('value'); $.each(curvalue, (k, v) => { Twinkle.config.listDialog.addRow($tbody, v.value, v.label); }); // save the old value $button.data('value', oldvalue); }; Twinkle.config.listDialog.save = function twinkleconfigListDialogSave($button, $tbody) { const result = []; let current = {}; $tbody.find('input[type="text"]').each((inputkey, input) => { if ($(input).hasClass('twinkle-config-customlist-value')) { current = { value: input.value }; } else { current.label = input.value; // exclude totally empty rows if (current.value || current.label) { result.push(current); } } }); $button.data('value', result); }; // reset/restore defaults Twinkle.config.resetPrefLink = function twinkleconfigResetPrefLink(e) { const wantedpref = e.target.id.slice(21); // "twinkle-config-reset-" prefix is stripped // search tactics $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // continue: skip impossibilities } let foundit = false; $(section.preferences).each((prefkey, pref) => { if (pref.name !== wantedpref) { return true; // continue } Twinkle.config.resetPref(pref); foundit = true; return false; // break }); if (foundit) { return false; // break } }); return false; // stop link from scrolling page }; Twinkle.config.resetPref = function twinkleconfigResetPref(pref) { switch (pref.type) { case 'boolean': document.getElementById(pref.name).checked = Twinkle.defaultConfig[pref.name]; break; case 'string': case 'integer': case 'enum': document.getElementById(pref.name).value = Twinkle.defaultConfig[pref.name]; break; case 'set': $.each(pref.setValues, (itemkey) => { if (document.getElementById(pref.name + '_' + itemkey)) { document.getElementById(pref.name + '_' + itemkey).checked = Twinkle.defaultConfig[pref.name].includes(itemkey); } }); break; case 'customList': $(document.getElementById(pref.name)).data('value', Twinkle.defaultConfig[pref.name]); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } }; Twinkle.config.resetAllPrefs = function twinkleconfigResetAllPrefs() { // no confirmation message - the user can just refresh/close the page to abort $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // continue: skip impossibilities } $(section.preferences).each((prefkey, pref) => { if (!pref.adminOnly || Morebits.userIsSysop) { Twinkle.config.resetPref(pref); } }); return true; }); return false; // stop link from scrolling page }; Twinkle.config.save = function twinkleconfigSave(e) { Morebits.Status.init(document.getElementById('twinkle-config-content')); const userjs = mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').user] + ':' + mw.config.get('wgUserName') + '/twinkleoptions.js'; const wikipediaPage = new Morebits.wiki.Page(userjs, 'Saving preferences to ' + userjs); wikipediaPage.setCallbackParameters(e.target); wikipediaPage.load(Twinkle.config.writePrefs); return false; }; Twinkle.config.writePrefs = function twinkleconfigWritePrefs(pageobj) { const form = pageobj.getCallbackParameters(); // this is the object which gets serialized into JSON; only // preferences that this script knows about are kept const newConfig = {optionsVersion: 2.1}; // a comparison function is needed later on // it is just enough for our purposes (i.e. comparing strings, numbers, booleans, // arrays of strings, and arrays of { value, label }) // and it is not very robust: e.g. compare([2], ["2"]) === true, and // compare({}, {}) === false, but it's good enough for our purposes here const compare = function(a, b) { if (Array.isArray(a)) { if (a.length !== b.length) { return false; } const asort = a.sort(), bsort = b.sort(); for (let i = 0; asort[i]; ++i) { // comparison of the two properties of custom lists if ((typeof asort[i] === 'object') && (asort[i].label !== bsort[i].label || asort[i].value !== bsort[i].value)) { return false; } else if (asort[i].toString() !== bsort[i].toString()) { return false; } } return true; } return a === b; }; $(Twinkle.config.sections).each((sectionkey, section) => { if (section.adminOnly && !Morebits.userIsSysop) { return; // i.e. "continue" in this context } // reach each of the preferences from the form $(section.preferences).each((prefkey, pref) => { let userValue; // = undefined // only read form values for those prefs that have them if (!pref.adminOnly || Morebits.userIsSysop) { if (!section.hidden) { switch (pref.type) { case 'boolean': // read from the checkbox userValue = form[pref.name].checked; break; case 'string': // read from the input box or combo box case 'enum': userValue = form[pref.name].value; break; case 'integer': // read from the input box userValue = parseInt(form[pref.name].value, 10); if (isNaN(userValue)) { Morebits.Status.warn('Saving', 'The value you specified for ' + pref.name + ' (' + pref.value + ') was invalid. The save will continue, but the invalid data value will be skipped.'); userValue = null; } break; case 'set': // read from the set of check boxes userValue = []; if (pref.setDisplayOrder) { // read only those keys specified in the display order $.each(pref.setDisplayOrder, (itemkey, item) => { if (form[pref.name + '_' + item].checked) { userValue.push(item); } }); } else { // read all the keys in the list of values $.each(pref.setValues, (itemkey) => { if (form[pref.name + '_' + itemkey].checked) { userValue.push(itemkey); } }); } break; case 'customList': // read from the jQuery data stored on the button object userValue = $(form[pref.name]).data('value'); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } } else if (Twinkle.prefs) { // Retain the hidden preferences that may have customised by the user from twinkleoptions.js // undefined if not set userValue = Twinkle.prefs[pref.name]; } } // only save those preferences that are *different* from the default if (userValue !== undefined && !compare(userValue, Twinkle.defaultConfig[pref.name])) { newConfig[pref.name] = userValue; } }); }); let text = '// twinkleoptions.js: personal Twinkle preferences file\n' + '//\n' + '// NOTE: The easiest way to change your Twinkle preferences is by using the\n' + '// Twinkle preferences panel, at [[' + Morebits.pageNameNorm + ']].\n' + '//\n' + '// This file is AUTOMATICALLY GENERATED. Any changes you make (aside from\n' + '// changing the configuration parameters in a valid-JavaScript way) will be\n' + '// overwritten the next time you click "save" in the Twinkle preferences\n' + '// panel. If modifying this file, make sure to use correct JavaScript.\n' + // eslint-disable-next-line no-useless-concat '// <no' + 'wiki>\n' + '\n' + 'window.Twinkle.prefs = '; text += JSON.stringify(newConfig, null, 2); text += ';\n' + '\n' + // eslint-disable-next-line no-useless-concat '// </no' + 'wiki>\n' + '// End of twinkleoptions.js\n'; pageobj.setPageText(text); pageobj.setEditSummary('Saving Twinkle preferences: automatic edit from [[:' + Morebits.pageNameNorm + ']]'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('recreate'); pageobj.save(Twinkle.config.saveSuccess); }; Twinkle.config.saveSuccess = function twinkleconfigSaveSuccess(pageobj) { pageobj.getStatusElement().info('successful'); const noticebox = document.createElement('div'); noticebox.className = 'cdx-message cdx-message--success'; noticebox.style.fontSize = '100%'; noticebox.innerHTML = '<p><b>Your Twinkle preferences have been saved.</b> To see the changes, you will need to clear your browser cache entirely (see <a href="' + mw.util.getUrl('WP:BYPASS') + '" title="WP:BYPASS">WP:BYPASS</a> for instructions).</p>'; mw.loader.using('mediawiki.htmlform.codex.styles', () => { Morebits.Status.root.appendChild(noticebox); }); const noticeclear = document.createElement('br'); noticeclear.style.clear = 'both'; Morebits.Status.root.appendChild(noticeclear); }; Twinkle.addInitCallback(Twinkle.config.init); }()); // </nowiki> a3s7rfkqfx18l5b93fy769av25bi22y 365987 365986 2026-08-20T12:29:27Z ChiK 1136 L10n 365987 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinkleconfig.js: Preferences module **************************************** * Mode of invocation: Adds configuration form to ವಿಕಿಪೀಡಿಯ:ಟ್ವಿಂಕಲ್/ಪ್ರಾಸಸ್ತ್ಯೊಲು, and adds an ad box to the top of user subpages belonging to the currently logged-in user which end in '.js' * Active on: What I just said. Yeah. */ Twinkle.config = {}; Twinkle.config.watchlistEnums = { yes: 'Add to watchlist (indefinitely)', no: "Don't add to watchlist", default: 'Follow your site preferences', '1 week': 'Watch for 1 week', '1 month': 'Watch for 1 month', '3 months': 'Watch for 3 months', '6 months': 'Watch for 6 months' }; Twinkle.config.commonSets = { csdCriteria: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', c4: 'C4', f1: 'F1', f2: 'F2', f3: 'F3', f7: 'F7', f8: 'F8', f9: 'F9', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5', g6: 'G6', g7: 'G7', g8: 'G8', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', t5: 'T5', u1: 'U1', u2: 'U2', u6: 'U6', u7: 'U7' }, csdCriteriaNotification: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', f1: 'F1', f2: 'F2', f3: 'F3', f7: 'F7', f9: 'F9', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5 ("general sanction violation" only)', g6: 'G6 ("copy-paste move" only)', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', u6: 'U6', u7: 'U7' }, csdAndImageDeletionCriteria: { db: 'Custom rationale ({{db}})', a1: 'A1', a2: 'A2', a3: 'A3', a7: 'A7', a9: 'A9', a10: 'A10', a11: 'A11', c1: 'C1', c4: 'C4', f1: 'F1', f2: 'F2', f3: 'F3', f4: 'F4', f5: 'F5', f6: 'F6', f7: 'F7', f8: 'F8', f9: 'F9', f11: 'F11', g1: 'G1', g2: 'G2', g3: 'G3', g4: 'G4', g5: 'G5', g6: 'G6', g7: 'G7', g8: 'G8', g10: 'G10', g11: 'G11', g12: 'G12', g13: 'G13', g14: 'G14', g15: 'G15', r2: 'R2', r3: 'R3', r4: 'R4', t5: 'T5', u1: 'U1', u2: 'U2', u6: 'U6', u7: 'U7' }, namespacesNoSpecial: { 0: 'Article', 1: 'Talk (article)', 2: 'User', 3: 'User talk', 4: 'Wikipedia', 5: 'Wikipedia talk', 6: 'File', 7: 'File talk', 8: 'MediaWiki', 9: 'MediaWiki talk', 10: 'Template', 11: 'Template talk', 12: 'Help', 13: 'Help talk', 14: 'Category', 15: 'Category talk', 100: 'Portal', 101: 'Portal talk', 118: 'Draft', 119: 'Draft talk', 710: 'TimedText', 711: 'TimedText talk', 828: 'Module', 829: 'Module talk' } }; Twinkle.config.commonSets.csdCriteriaDisplayOrder = Object.keys(Twinkle.config.commonSets.csdCriteria); Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder = Object.keys(Twinkle.config.commonSets.csdCriteriaNotification); Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder = Object.keys(Twinkle.config.commonSets.csdAndImageDeletionCriteria); /** * Section entry format: * * { * title: <human-readable section title>, * module: <name of the associated module, used to link to sections>, * adminOnly: <true for admin-only sections>, * hidden: <true for advanced preferences that rarely need to be changed - they can still be modified by manually editing twinkleoptions.js>, * preferences: [ * { * name: <TwinkleConfig property name>, * label: <human-readable short description - used as a form label>, * helptip: <(optional) human-readable text (using valid HTML) that complements the description, like limits, warnings, etc.> * adminOnly: <true for admin-only preferences>, * type: <string|boolean|integer|enum|set|customList> (customList stores an array of JSON objects { value, label }), * enumValues: <for type = "enum": a JSON object where the keys are the internal names and the values are human-readable strings>, * setValues: <for type = "set": a JSON object where the keys are the internal names and the values are human-readable strings>, * setDisplayOrder: <(optional) for type = "set": an array containing the keys of setValues (as strings) in the order that they are displayed>, * customListValueTitle: <for type = "customList": the heading for the left "value" column in the custom list editor>, * customListLabelTitle: <for type = "customList": the heading for the right "label" column in the custom list editor> * }, * . . . * ] * }, * . . . * */ Twinkle.config.sections = [ { title: 'General', module: 'general', preferences: [ // TwinkleConfig.userTalkPageMode may take arguments: // 'window': open a new window, remember the opened window // 'tab': opens in a new tab, if possible. // 'blank': force open in a new window, even if such a window exists { name: 'userTalkPageMode', label: 'When opening a user talk page, open it', type: 'enum', enumValues: { window: 'In a window, replacing other user talks', tab: 'In a new tab', blank: 'In a totally new window' } }, // TwinkleConfig.dialogLargeFont (boolean) { name: 'dialogLargeFont', label: 'Use larger text in Twinkle dialogs', type: 'boolean' }, // Twinkle.config.disabledModules (array) { name: 'disabledModules', label: 'Turn off the selected Twinkle modules', helptip: 'Anything you select here will NOT be available for use, so act with care. Uncheck to reactivate.', type: 'set', setValues: { arv: 'ARV', warn: 'Warn', welcome: 'Welcome', talkback: 'Talkback', speedy: 'CSD', prod: 'PROD', xfd: 'XfD', image: 'Image (DI)', protect: 'Protect (RPP)', tag: 'Tag', diff: 'Diff', unlink: 'Unlink', rollback: 'Revert and rollback' } }, // Twinkle.config.disabledSysopModules (array) { name: 'disabledSysopModules', label: 'Turn off the selected admin-only modules', helptip: 'Anything you select here will NOT be available for use, so act with care. Uncheck to reactivate.', adminOnly: true, type: 'set', setValues: { block: 'Block', deprod: 'DePROD', batchdelete: 'D-batch', batchprotect: 'P-batch', batchundelete: 'Und-batch' } } ] }, { title: 'ARV', module: 'arv', preferences: [ { name: 'spiWatchReport', label: 'Add sockpuppet report pages to watchlist', type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Block user', module: 'block', adminOnly: true, preferences: [ // TwinkleConfig.defaultToBlock64 (boolean) // Whether to default to just blocking the /64 on or off { name: 'defaultToBlock64', label: 'For IPv6 addresses, select the option to block the /64 range by default', type: 'boolean' }, // TwinkleConfig.defaultToPartialBlocks (boolean) // Whether to default partial blocks on or off { name: 'defaultToPartialBlocks', label: 'Select partial blocks by default when opening the block menu', helptip: 'If the user is already blocked, this will be overridden in favor of defaulting to the current block type', type: 'boolean' }, // TwinkleConfig.blankTalkpageOnIndefBlock (boolean) // if true, blank the talk page when issuing an indef block notice (per [[WP:UWUL#Indefinitely blocked users]]) { name: 'blankTalkpageOnIndefBlock', label: 'Blank the talk page when indefinitely blocking users', helptip: 'See <a href="' + mw.util.getUrl('Wikipedia:WikiProject_User_warnings/Usage_and_layout#Indefinitely_blocked_users') + '">WP:UWUL</a> for more information.', type: 'boolean' } ] }, { title: 'Image deletion (DI)', module: 'image', preferences: [ // TwinkleConfig.notifyUserOnDeli (boolean) // If the user should be notified after placing a file deletion tag { name: 'notifyUserOnDeli', label: 'Check the "notify initial uploader" box by default', type: 'boolean' }, // TwinkleConfig.deliWatchPage (string) // The watchlist setting of the page tagged for deletion. { name: 'deliWatchPage', label: 'Add image page to watchlist when tagging', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.deliWatchUser (string) // The watchlist setting of the user talk page if a notification is placed. { name: 'deliWatchUser', label: 'Add user talk page of initial uploader to watchlist when notifying', type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Page protection ' + (Morebits.userIsSysop ? '(PP)' : '(RPP)'), module: 'protect', preferences: [ { name: 'watchRequestedPages', label: 'Add page to watchlist when requesting protection', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchPPTaggedPages', label: 'Add page to watchlist when tagging with protection template', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchProtectedPages', label: 'Add page to watchlist when protecting', helptip: 'If also tagging the page after protection, that preference will be favored.', adminOnly: true, type: 'enum', enumValues: Twinkle.config.watchlistEnums } ] }, { title: 'Proposed deletion (PROD)', module: 'prod', preferences: [ // TwinkleConfig.watchProdPages (string) // Watchlist setting when applying prod template to page { name: 'watchProdPages', label: 'Add article to watchlist when tagging', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.markProdPagesAsPatrolled (boolean) // If, when applying prod template to page, to mark the page as curated/patrolled (if the page was reached from NewPages) { name: 'markProdPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when tagging (if possible)', helptip: 'This should probably not be checked as doing so is against best practice consensus', type: 'boolean' }, // TwinkleConfig.prodReasonDefault (string) // The prefilled PROD reason. { name: 'prodReasonDefault', label: 'Prefilled PROD reason', type: 'string' }, { name: 'logProdPages', label: 'Keep a log in userspace of all pages you tag for PROD', helptip: 'Since non-admins do not have access to their deleted contributions, the userspace log offers a good way to keep track of all pages you tag for PROD using Twinkle.', type: 'boolean' }, { name: 'prodLogPageName', label: 'Keep the PROD userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your PROD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the PROD userspace log.', type: 'string' } ] }, { title: 'Revert and rollback', module: 'rollback', preferences: [ // TwinkleConfig.autoMenuAfterRollback (bool) // Option to automatically open the warning menu if the user talk page is opened post-reversion { name: 'autoMenuAfterRollback', label: 'Automatically open the Twinkle warn menu on a user talk page after Twinkle rollback', helptip: 'Only operates if the relevant box is checked below.', type: 'boolean' }, // TwinkleConfig.openTalkPage (array) // What types of actions that should result in opening of talk page { name: 'openTalkPage', label: 'Open user talk page after these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback' } }, // TwinkleConfig.openTalkPageOnAutoRevert (bool) // Defines if talk page should be opened when calling revert from contribs or recent changes pages. If set to true, openTalkPage defines then if talk page will be opened. { name: 'openTalkPageOnAutoRevert', label: 'Open user talk page when invoking rollback from user contributions or recent changes', helptip: 'When this is on, the desired options must be enabled in the previous setting for this to work.', type: 'boolean' }, // TwinkleConfig.rollbackInPlace (bool) // { name: 'rollbackInPlace', label: "Don't reload the page when rolling back from contributions or recent changes", helptip: "When this is on, Twinkle won't reload the contributions or recent changes feed after reverting, allowing you to revert more than one edit at a time.", type: 'boolean' }, // TwinkleConfig.markRevertedPagesAsMinor (array) // What types of actions that should result in marking edit as minor { name: 'markRevertedPagesAsMinor', label: 'Mark as minor edit for these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback', torev: '"Restore this version"' } }, // TwinkleConfig.watchRevertedPages (array) // What types of actions that should result in forced addition to watchlist { name: 'watchRevertedPages', label: 'Add pages to watchlist for these types of reversions', type: 'set', setValues: { agf: 'AGF rollback', norm: 'Normal rollback', vand: 'Vandalism rollback', torev: '"Restore this version"' } }, // TwinkleConfig.watchRevertedExpiry // If any of the above items are selected, whether to expire the watch { name: 'watchRevertedExpiry', label: 'When reverting a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.offerReasonOnNormalRevert (boolean) // If to offer a prompt for extra summary reason for normal reverts, default to true { name: 'offerReasonOnNormalRevert', label: 'Prompt for reason for normal rollbacks', helptip: '"Normal" rollbacks are the ones that are invoked from the middle [rollback] link.', type: 'boolean' }, { name: 'confirmOnRollback', label: 'Require confirmation before reverting (all devices)', helptip: 'For users of pen or touch devices, and chronically indecisive people.', type: 'boolean' }, { name: 'confirmOnMobileRollback', label: 'Require confirmation before reverting (mobile devices only)', helptip: 'Avoid accidental reversions when on mobile devices.', type: 'boolean' }, // TwinkleConfig.showRollbackLinks (array) // Where Twinkle should show rollback links: // diff, others, mine, contribs, history, recent // Note from TTO: |contribs| seems to be equal to |others| + |mine|, i.e. redundant, so I left it out heres { name: 'showRollbackLinks', label: 'Show rollback links on these pages', type: 'set', setValues: { diff: 'Diff pages', others: 'Contributions pages of other users', mine: 'My contributions page', recent: 'Recent changes and related changes special pages', history: 'History pages' } } ] }, { title: 'Speedy deletion (CSD)', module: 'speedy', preferences: [ { name: 'speedySelectionStyle', label: 'When to go ahead and tag/delete the page', type: 'enum', enumValues: { buttonClick: 'When I click "Submit"', radioClick: 'As soon as I click an option' } }, // TwinkleConfig.watchSpeedyPages (array) // Whether to add speedy tagged or deleted pages to watchlist { name: 'watchSpeedyPages', label: 'Add page to watchlist when using these criteria', type: 'set', setValues: Twinkle.config.commonSets.csdCriteria, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaDisplayOrder }, // TwinkleConfig.watchSpeedyExpiry // If any of the above items are selected, whether to expire the watch { name: 'watchSpeedyExpiry', label: 'When tagging a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.markSpeedyPagesAsPatrolled (boolean) // If, when applying speedy template to page, to mark the page as triaged/patrolled (if the page was reached from NewPages) { name: 'markSpeedyPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when tagging (if possible)', helptip: 'This should probably not be checked as doing so is against best practice consensus', type: 'boolean' }, // TwinkleConfig.watchSpeedyUser (string) // The watchlist setting of the user talk page if they receive a notification. { name: 'watchSpeedyUser', label: 'Add user talk page of initial contributor to watchlist (when notifying)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.welcomeUserOnSpeedyDeletionNotification (array of strings) // On what types of speedy deletion notifications shall the user be welcomed // with a "firstarticle" notice if their talk page has not yet been created. { name: 'welcomeUserOnSpeedyDeletionNotification', label: 'Welcome page creator when notifying with these criteria', helptip: 'The welcome is issued only if the user is notified about the deletion, and only if their talk page does not already exist. The template used is {{firstarticle}}.', type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.notifyUserOnSpeedyDeletionNomination (array) // What types of actions should result in the author of the page being notified of nomination { name: 'notifyUserOnSpeedyDeletionNomination', label: 'Notify page creator when tagging with these criteria', helptip: 'Even if you choose to notify from the CSD screen, the notification will only take place for those criteria selected here.', type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.warnUserOnSpeedyDelete (array) // What types of actions should result in the author of the page being notified of speedy deletion (admin only) { name: 'warnUserOnSpeedyDelete', label: 'Notify page creator when deleting under these criteria', helptip: 'Even if you choose to notify from the CSD screen, the notification will only take place for those criteria selected here.', adminOnly: true, type: 'set', setValues: Twinkle.config.commonSets.csdCriteriaNotification, setDisplayOrder: Twinkle.config.commonSets.csdCriteriaNotificationDisplayOrder }, // TwinkleConfig.promptForSpeedyDeletionSummary (array of strings) { name: 'promptForSpeedyDeletionSummary', label: 'Allow editing of deletion summary when deleting under these criteria', adminOnly: true, type: 'set', setValues: Twinkle.config.commonSets.csdAndImageDeletionCriteria, setDisplayOrder: Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder }, // TwinkleConfig.deleteTalkPageOnDelete (boolean) // If talk page if exists should also be deleted (CSD G8) when spedying a page (admin only) { name: 'deleteTalkPageOnDelete', label: 'Check the "also delete talk page" box by default', adminOnly: true, type: 'boolean' }, { name: 'deleteRedirectsOnDelete', label: 'Check the "also delete redirects" box by default', adminOnly: true, type: 'boolean' }, // TwinkleConfig.deleteSysopDefaultToDelete (boolean) // Make the CSD screen default to "delete" instead of "tag" (admin only) { name: 'deleteSysopDefaultToDelete', label: 'Default to outright deletion instead of speedy tagging', helptip: 'If there is a CSD tag already present, Twinkle will always default to "delete" mode', adminOnly: true, type: 'boolean' }, // TwinkleConfig.speedyWindowWidth (integer) // Defines the width of the Twinkle SD window in pixels { name: 'speedyWindowWidth', label: 'Width of speedy deletion window (pixels)', type: 'integer' }, // TwinkleConfig.speedyWindowWidth (integer) // Defines the width of the Twinkle SD window in pixels { name: 'speedyWindowHeight', label: 'Height of speedy deletion window (pixels)', helptip: 'If you have a big monitor, you might like to increase this.', type: 'integer' }, { name: 'logSpeedyNominations', label: 'Keep a log in userspace of all CSD nominations', helptip: 'Since non-admins do not have access to their deleted contributions, the userspace log offers a good way to keep track of all pages you nominate for CSD using Twinkle. Files tagged using DI are also added to this log.', type: 'boolean' }, { name: 'speedyLogPageName', label: 'Keep the CSD userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your CSD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the CSD userspace log.', type: 'string' }, { name: 'noLogOnSpeedyNomination', label: 'Do not create a userspace log entry when tagging with these criteria', type: 'set', setValues: Twinkle.config.commonSets.csdAndImageDeletionCriteria, setDisplayOrder: Twinkle.config.commonSets.csdAndImageDeletionCriteriaDisplayOrder } ] }, { title: 'Tag', module: 'tag', preferences: [ { name: 'watchTaggedVenues', label: 'Add page to watchlist when tagging these type of pages', type: 'set', setValues: { articles: 'Articles', drafts: 'Drafts', redirects: 'Redirects', files: 'Files' } }, { name: 'watchTaggedPages', label: 'When tagging a page, how long to watch it for', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'watchMergeDiscussions', label: 'Add talk pages to watchlist when starting merge discussions', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'markTaggedPagesAsMinor', label: 'Mark addition of tags as a minor edit', type: 'boolean' }, { name: 'markTaggedPagesAsPatrolled', label: 'Check the "mark page as patrolled/reviewed" box by default', type: 'boolean' }, { name: 'groupByDefault', label: 'Check the "group into {{multiple issues}}" box by default', type: 'boolean' }, { name: 'tagArticleSortOrder', label: 'Default view order for article tags', type: 'enum', enumValues: { cat: 'By categories', alpha: 'In alphabetical order' } }, { name: 'customTagList', label: 'Custom article/draft maintenance tags to display', helptip: "These appear as additional options at the bottom of the list of tags. For example, you could add new maintenance tags which have not yet been added to Twinkle's defaults.", type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' }, { name: 'customFileTagList', label: 'Custom file maintenance tags to display', helptip: 'Additional tags that you wish to add for files.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' }, { name: 'customRedirectTagList', label: 'Custom redirect category tags to display', helptip: 'Additional tags that you wish to add for redirects.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Tag dialog' } ] }, { title: 'Talkback', module: 'talkback', preferences: [ { name: 'markTalkbackAsMinor', label: 'Mark talkbacks as minor edits', type: 'boolean' }, { name: 'insertTalkbackSignature', label: 'Insert signature within talkbacks', type: 'boolean' }, { name: 'talkbackHeading', label: 'Section heading to use for talkback and please see', tooltip: 'Should NOT include the equals signs ("==") used for wikitext formatting', type: 'string' }, { name: 'mailHeading', label: "Section heading to use for \"you've got mail\" notices", tooltip: 'Should NOT include the equals signs ("==") used for wikitext formatting', type: 'string' } ] }, { title: 'Unlink', module: 'unlink', preferences: [ // TwinkleConfig.unlinkNamespaces (array) // In what namespaces unlink should happen, default in 0 (article), 10 (template), 100 (portal), and 118 (draft) { name: 'unlinkNamespaces', label: 'Remove links from pages in these namespaces', helptip: 'Avoid selecting any talk namespaces, as Twinkle might end up unlinking on talk archives (a big no-no).', type: 'set', setValues: Twinkle.config.commonSets.namespacesNoSpecial } ] }, { title: 'Warn user', module: 'warn', preferences: [ // TwinkleConfig.defaultWarningGroup (int) // Which level warning should be the default selected group, default is 1 { name: 'defaultWarningGroup', label: 'Default warning level', type: 'enum', enumValues: { 1: 'Level 1', 2: 'Level 2', 3: 'Level 3', 4: 'Level 4', 5: 'Level 4im', 6: 'Single-issue notices', 7: 'Single-issue warnings', // 8 was used for block templates before #260 9: 'Custom warnings', 10: 'All warning templates', 11: 'Auto-select level (1-4)' } }, // TwinkleConfig.combinedSingletMenus (boolean) // if true, show one menu with both single-issue notices and warnings instead of two separately { name: 'combinedSingletMenus', label: 'Replace the two separate single-issue menus into one combined menu', helptip: 'Selecting either single-issue notices or single-issue warnings as your default will make this your default if enabled.', type: 'boolean' }, // TwinkleConfig.watchWarnings (string) // Watchlist setting for the page which has been dispatched an warning or notice { name: 'watchWarnings', label: 'Add user talk page to watchlist when notifying', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.oldSelect (boolean) // if true, use the native select menu rather the select2-based one { name: 'oldSelect', label: 'Use the non-searchable classic select menu', type: 'boolean' }, { name: 'customWarningList', label: 'Custom warning templates to display', helptip: 'You can add individual templates or user subpages. Custom warnings appear in the "Custom warnings" category within the warning dialog box.', type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in warning list (also used as edit summary)' } ] }, { title: 'Welcome user', module: 'welcome', preferences: [ { name: 'topWelcomes', label: 'Place welcomes above existing content on user talk pages', type: 'boolean' }, { name: 'watchWelcomes', label: 'Add user talk pages to watchlist when welcoming', helptip: 'Doing so adds to the personal element of welcoming a user - you will be able to see how they are coping as a newbie, and possibly help them.', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'insertUsername', label: 'Add your username to the template (where applicable)', helptip: "Some welcome templates have an opening sentence like \"Hi, I'm &lt;username&gt;. Welcome\" etc. If you turn off this option, these templates will not display your username in that way.", type: 'boolean' }, { name: 'quickWelcomeMode', label: 'Clicking the "welcome" link on a diff page (which only appears if the editor\'s user talk page has not been created yet) will', helptip: 'If you choose to welcome automatically, the template you specify below will be used.', type: 'enum', enumValues: { auto: 'immediately post the welcome template specified below', norm: 'prompt you to select a template' } }, { name: 'quickWelcomeTemplate', label: 'Template to use when welcoming automatically', helptip: 'Enter the name of a welcome template, without the curly brackets. A link to the given article will be added.', type: 'string' }, { name: 'customWelcomeList', label: 'Custom welcome templates to display', helptip: "You can add other welcome templates, or user subpages that are welcome templates (prefixed with \"User:\"). Don't forget that these templates are substituted onto user talk pages.", type: 'customList', customListValueTitle: 'Template name (no curly brackets)', customListLabelTitle: 'Text to show in Welcome dialog' }, { name: 'customWelcomeSignature', label: 'Automatically sign custom welcome templates', helptip: 'If your custom welcome templates contain a built-in signature within the template, turn off this option.', type: 'boolean' } ] }, { title: 'XFD (deletion discussions)', module: 'xfd', preferences: [ { name: 'logXfdNominations', label: 'Keep a log in userspace of all pages you nominate for a deletion discussion (XfD)', helptip: 'The userspace log offers a good way to keep track of all pages you nominate for XfD using Twinkle.', type: 'boolean' }, { name: 'xfdLogPageName', label: 'Keep the deletion discussion userspace log at this user subpage', helptip: 'Enter a subpage name in this box. You will find your XfD log at User:<i>username</i>/<i>subpage name</i>. Only works if you turn on the XfD userspace log.', type: 'string' }, { name: 'noLogOnXfdNomination', label: 'Do not create a userspace log entry when nominating at this venue', type: 'set', setValues: { afd: 'AfD', tfd: 'TfD', ffd: 'FfD', cfd: 'CfD', cfds: 'CfD/S', mfd: 'MfD', rfd: 'RfD', rm: 'RM' } }, // TwinkleConfig.xfdWatchPage (string) // The watchlist setting of the page being nominated for XfD. { name: 'xfdWatchPage', label: 'Add the nominated page to watchlist', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchDiscussion (string) // The watchlist setting of the newly created XfD page (for those processes that create discussion pages for each nomination), // or the list page for the other processes. { name: 'xfdWatchDiscussion', label: 'Add the deletion discussion page to watchlist', helptip: 'This refers to the discussion subpage (for AfD and MfD) or the daily log page (for TfD, CfD, RfD and FfD)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchList (string) // The watchlist setting of the XfD list page, *if* the discussion is on a separate page. { name: 'xfdWatchList', label: 'Add the daily log/list page to the watchlist (AfD and MfD)', helptip: 'This only applies for AfD and MfD, where the discussions are transcluded onto a daily log page (for AfD) or the main MfD page (for MfD).', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchUser (string) // The watchlist setting of the user talk page if they receive a notification. { name: 'xfdWatchUser', label: 'Add user talk page of initial contributor to watchlist (when notifying)', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, // TwinkleConfig.xfdWatchRelated (string) // The watchlist setting of the target of a redirect being nominated for RfD. { name: 'xfdWatchRelated', label: "Add the redirect's target page to watchlist (when notifying)", helptip: 'This only applies for RfD, when leaving a notification on the talk page of the target of the redirect', type: 'enum', enumValues: Twinkle.config.watchlistEnums }, { name: 'markXfdPagesAsPatrolled', label: 'Mark page as patrolled/reviewed when nominating for AFD (if possible)', type: 'boolean' } ] }, { title: 'Hidden', hidden: true, preferences: [ // twinklerollback.js: defines how many revision to query maximum, maximum possible is 50, default is 50 { name: 'revertMaxRevisions', type: 'integer' }, // twinklewarn.js: When using the autolevel select option, how many days makes a prior warning stale // Huggle is three days ([[Special:Diff/918980316]] and [[Special:Diff/919417999]]) while ClueBotNG is two: // https://github.com/DamianZaremba/cluebotng/blob/4958e25d6874cba01c75f11debd2e511fd5a2ce5/bot/action_functions.php#L62 { name: 'autolevelStaleDays', type: 'integer' }, // How many pages should be queried by deprod and batchdelete/protect/undelete { name: 'batchMax', type: 'integer', adminOnly: true }, // How many pages should be processed at a time by deprod and batchdelete/protect/undelete { name: 'batchChunks', type: 'integer', adminOnly: true } ] } ]; // end of Twinkle.config.sections Twinkle.config.init = function twinkleconfigInit() { // create the config page at ವಿಕಿಪೀಡಿಯ:ಟ್ವಿಂಕಲ್/ಪ್ರಾಸಸ್ತ್ಯೊಲು if ((mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').project && mw.config.get('wgTitle') === 'ಟ್ವಿಂಕಲ್/ಪ್ರಾಸಸ್ತ್ಯೊಲು') && mw.config.get('wgAction') === 'view') { if (!document.getElementById('twinkle-config')) { return; // maybe the page is misconfigured, or something - but any attempt to modify it will be pointless } // set style to nothing to prevent conflict with external css document.getElementById('twinkle-config').removeAttribute('style'); document.getElementById('twinkle-config-titlebar').removeAttribute('style'); const contentdiv = document.getElementById('twinkle-config-content'); contentdiv.textContent = ''; // clear children // let user know about possible conflict with skin js/common.js file // (settings in that file will still work, but they will be overwritten by twinkleoptions.js settings) if (window.TwinkleConfig || window.FriendlyConfig) { const contentnotice = document.createElement('div'); contentnotice.className = 'plainlinks twinkle-ombox'; contentnotice.innerHTML = '<div>' + '<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/3/38/Imbox_content.png" />' + '</div>' + '<div>' + '<p><big><b>Before modifying your settings here,</b> you must remove your old Twinkle and Friendly settings from your personal skin JavaScript.</big></p>' + '<p>To do this, you can <a href="' + mw.util.getUrl('User:' + mw.config.get('wgUserName') + '/' + mw.config.get('skin') + '.js', { action: 'edit' }) + '" target="_blank"><b>edit your personal skin javascript file</b></a> or <a href="' + mw.util.getUrl('User:' + mw.config.get('wgUserName') + '/common.js', { action: 'edit'}) + '" target="_blank"><b>your common.js file</b></a>, removing all lines of code that refer to <code>TwinkleConfig</code> and <code>FriendlyConfig</code>.</p>' + '</div>'; contentdiv.appendChild(contentnotice); } // start a table of contents const toctable = document.createElement('div'); toctable.className = 'toc'; toctable.style.marginLeft = '0.4em'; // create TOC title const toctitle = document.createElement('div'); toctitle.id = 'toctitle'; const toch2 = document.createElement('h2'); toch2.textContent = 'Contents '; toctitle.appendChild(toch2); // add TOC show/hide link const toctoggle = document.createElement('span'); toctoggle.className = 'toctoggle'; toctoggle.appendChild(document.createTextNode('[')); const toctogglelink = document.createElement('a'); toctogglelink.className = 'internal'; toctogglelink.setAttribute('href', '#tw-tocshowhide'); toctogglelink.textContent = 'hide'; toctoggle.appendChild(toctogglelink); toctoggle.appendChild(document.createTextNode(']')); toctitle.appendChild(toctoggle); toctable.appendChild(toctitle); // create item container: this is what we add stuff to const tocul = document.createElement('ul'); toctogglelink.addEventListener('click', () => { const $tocul = $(tocul); $tocul.toggle(); if ($tocul.find(':visible').length) { toctogglelink.textContent = 'hide'; } else { toctogglelink.textContent = 'show'; } }, false); toctable.appendChild(tocul); contentdiv.appendChild(toctable); const contentform = document.createElement('form'); contentform.setAttribute('action', 'javascript:void(0)'); // was #tw-save - changed to void(0) to work around Chrome issue contentform.addEventListener('submit', Twinkle.config.save, true); contentdiv.appendChild(contentform); const container = document.createElement('table'); container.style.width = '100%'; contentform.appendChild(container); $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // i.e. "continue" in this context } // add to TOC const tocli = document.createElement('li'); tocli.className = 'toclevel-1'; const toca = document.createElement('a'); toca.setAttribute('href', '#' + section.module); toca.appendChild(document.createTextNode(section.title)); tocli.appendChild(toca); tocul.appendChild(tocli); let row = document.createElement('tr'); let cell = document.createElement('td'); cell.setAttribute('colspan', '3'); const heading = document.createElement('h4'); heading.style.borderBottom = '1px solid gray'; heading.style.marginTop = '0.2em'; heading.id = section.module; heading.appendChild(document.createTextNode(section.title)); cell.appendChild(heading); row.appendChild(cell); container.appendChild(row); let rowcount = 1; // for row banding // add each of the preferences to the form $(section.preferences).each((prefkey, pref) => { if (pref.adminOnly && !Morebits.userIsSysop) { return true; // i.e. "continue" in this context } row = document.createElement('tr'); row.style.marginBottom = '0.2em'; // create odd row banding if (rowcount++ % 2 === 0) { row.style.backgroundColor = 'rgba(128, 128, 128, 0.1)'; } cell = document.createElement('td'); let label, input; const gotPref = Twinkle.getPref(pref.name); switch (pref.type) { case 'boolean': // create a checkbox cell.setAttribute('colspan', '2'); label = document.createElement('label'); input = document.createElement('input'); input.setAttribute('type', 'checkbox'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); if (gotPref === true) { input.setAttribute('checked', 'checked'); } label.appendChild(input); label.appendChild(document.createTextNode(pref.label)); cell.appendChild(label); break; case 'string': // create an input box case 'integer': // add label to first column cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add input box to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; input = document.createElement('input'); input.setAttribute('type', 'text'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); if (pref.type === 'integer') { input.setAttribute('size', 6); input.setAttribute('type', 'number'); input.setAttribute('step', '1'); // integers only } if (gotPref) { input.setAttribute('value', gotPref); } cell.appendChild(input); break; case 'enum': // create a combo box // add label to first column // note: duplicates the code above, under string/integer cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add input box to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; input = document.createElement('select'); input.setAttribute('id', pref.name); input.setAttribute('name', pref.name); $.each(pref.enumValues, (enumvalue, enumdisplay) => { const option = document.createElement('option'); option.setAttribute('value', enumvalue); if ((gotPref === enumvalue) || // Hack to convert old boolean watchlist prefs // to corresponding enums (added in v2.1) (typeof gotPref === 'boolean' && ((gotPref && enumvalue === 'yes') || (!gotPref && enumvalue === 'no')))) { option.setAttribute('selected', 'selected'); } option.appendChild(document.createTextNode(enumdisplay)); input.appendChild(option); }); cell.appendChild(input); break; case 'set': // create a set of check boxes // add label first of all cell.setAttribute('colspan', '2'); label = document.createElement('label'); // not really necessary to use a label element here, but we do it for consistency of styling label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); var checkdiv = document.createElement('div'); checkdiv.style.paddingLeft = '1em'; var worker = function(itemkey, itemvalue) { const checklabel = document.createElement('label'); checklabel.style.marginRight = '0.7em'; checklabel.style.display = 'inline-block'; const check = document.createElement('input'); check.setAttribute('type', 'checkbox'); check.setAttribute('id', pref.name + '_' + itemkey); check.setAttribute('name', pref.name + '_' + itemkey); if (gotPref && gotPref.includes(itemkey)) { check.setAttribute('checked', 'checked'); } // cater for legacy integer array values for unlinkNamespaces (this can be removed a few years down the track...) if (pref.name === 'unlinkNamespaces') { if (gotPref && gotPref.includes(parseInt(itemkey, 10))) { check.setAttribute('checked', 'checked'); } } checklabel.appendChild(check); checklabel.appendChild(document.createTextNode(itemvalue)); checkdiv.appendChild(checklabel); }; if (pref.setDisplayOrder) { // add check boxes according to the given display order $.each(pref.setDisplayOrder, (itemkey, item) => { worker(item, pref.setValues[item]); }); } else { // add check boxes according to the order it gets fed to us (probably strict alphabetical) $.each(pref.setValues, worker); } cell.appendChild(checkdiv); break; case 'customList': // add label to first column cell.style.textAlign = 'right'; cell.style.paddingRight = '0.5em'; label = document.createElement('label'); label.setAttribute('for', pref.name); label.appendChild(document.createTextNode(pref.label + ':')); cell.appendChild(label); row.appendChild(cell); // add button to second column cell = document.createElement('td'); cell.style.paddingRight = '1em'; var button = document.createElement('button'); button.setAttribute('id', pref.name); button.setAttribute('name', pref.name); button.setAttribute('type', 'button'); button.addEventListener('click', Twinkle.config.listDialog.display, false); // use jQuery data on the button to store the current config value $(button).data({ value: gotPref, pref: pref }); button.appendChild(document.createTextNode('Edit items')); cell.appendChild(button); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } row.appendChild(cell); // add help tip cell = document.createElement('td'); cell.className = 'twinkle-config-helptip'; if (pref.helptip) { // convert mentions of templates in the helptip to clickable links cell.innerHTML = pref.helptip.replace(/{{(.+?)}}/g, '{{<a href="' + mw.util.getUrl('Template:') + '$1" target="_blank">$1</a>}}'); } // add reset link (custom lists don't need this, as their config value isn't displayed on the form) if (pref.type !== 'customList') { const resetlink = document.createElement('a'); resetlink.setAttribute('href', '#tw-reset'); resetlink.setAttribute('id', 'twinkle-config-reset-' + pref.name); resetlink.addEventListener('click', Twinkle.config.resetPrefLink, false); resetlink.style.cssFloat = 'right'; resetlink.style.margin = '0 0.6em'; resetlink.appendChild(document.createTextNode('Reset')); cell.appendChild(resetlink); } row.appendChild(cell); container.appendChild(row); return true; }); return true; }); const footerbox = document.createElement('div'); footerbox.setAttribute('id', 'twinkle-config-buttonpane'); const button = document.createElement('button'); button.setAttribute('id', 'twinkle-config-submit'); button.setAttribute('type', 'submit'); button.appendChild(document.createTextNode('Save changes')); footerbox.appendChild(button); const footerspan = document.createElement('span'); footerspan.className = 'plainlinks'; footerspan.style.marginLeft = '2.4em'; footerspan.style.fontSize = '90%'; const footera = document.createElement('a'); footera.setAttribute('href', '#tw-reset-all'); footera.setAttribute('id', 'twinkle-config-resetall'); footera.addEventListener('click', Twinkle.config.resetAllPrefs, false); footera.appendChild(document.createTextNode('Restore defaults')); footerspan.appendChild(footera); footerbox.appendChild(footerspan); contentform.appendChild(footerbox); // since all the section headers exist now, we can try going to the requested anchor if (window.location.hash) { const loc = window.location.hash; window.location.hash = ''; window.location.hash = loc; } } else if (mw.config.get('wgNamespaceNumber') === mw.config.get('wgNamespaceIds').user && mw.config.get('wgTitle').indexOf(mw.config.get('wgUserName')) === 0 && mw.config.get('wgPageName').slice(-3) === '.js') { const box = document.createElement('div'); // Styled in twinkle.css box.setAttribute('id', 'twinkle-config-headerbox'); let link; const scriptPageName = mw.config.get('wgPageName').slice( mw.config.get('wgPageName').lastIndexOf('/') + 1, mw.config.get('wgPageName').lastIndexOf('.js') ); if (scriptPageName === 'twinkleoptions') { // place "why not try the preference panel" notice box.setAttribute('class', 'config-twopt-box'); if (mw.config.get('wgArticleId') > 0) { // page exists box.appendChild(document.createTextNode('This page contains your Twinkle preferences. You can change them using the ')); } else { // page does not exist box.appendChild(document.createTextNode('You can customize Twinkle to suit your preferences by using the ')); } link = document.createElement('a'); link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':ಟ್ವಿಂಕಲ್/ಪ್ರಾಸಸ್ತ್ಯೊಲು')); link.appendChild(document.createTextNode('Twinkle preferences panel')); box.appendChild(link); box.appendChild(document.createTextNode(', or by editing this page.')); $(box).insertAfter($('#contentSub')); } else if (['monobook', 'vector', 'vector-2022', 'cologneblue', 'modern', 'timeless', 'minerva', 'common'].includes(scriptPageName)) { // place "Looking for Twinkle options?" notice box.setAttribute('class', 'config-userskin-box'); box.appendChild(document.createTextNode('If you want to set Twinkle preferences, you can use the ')); link = document.createElement('a'); link.setAttribute('href', mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').project] + ':ಟ್ವಿಂಕಲ್/ಪ್ರಾಸಸ್ತ್ಯೊಲು')); link.appendChild(document.createTextNode('Twinkle preferences panel')); box.appendChild(link); box.appendChild(document.createTextNode('.')); $(box).insertAfter($('#contentSub')); } } }; // custom list-related stuff Twinkle.config.listDialog = {}; Twinkle.config.listDialog.addRow = function twinkleconfigListDialogAddRow($dlgtable, value, label) { let $contenttr, $valueInput, $labelInput; $dlgtable.append( $contenttr = $('<tr>').append( $('<td>').append( $('<button>') .attr('type', 'button') .on('click', () => { $contenttr.remove(); }) .text('Remove') ), $('<td>').append( $valueInput = $('<input>') .attr('type', 'text') .addClass('twinkle-config-customlist-value') .css('width', '97%') ), $('<td>').append( $labelInput = $('<input>') .attr('type', 'text') .addClass('twinkle-config-customlist-label') .css('width', '98%') ) ) ); if (value) { $valueInput.val(value); } if (label) { $labelInput.val(label); } }; Twinkle.config.listDialog.display = function twinkleconfigListDialogDisplay(e) { const $prefbutton = $(e.target); const curvalue = $prefbutton.data('value'); const curpref = $prefbutton.data('pref'); const dialog = new Morebits.SimpleWindow(720, 400); dialog.setTitle(curpref.label); dialog.setScriptName('Twinkle preferences'); let $dlgtbody; dialog.setContent( $('<div>').append( $('<table>') .addClass('wikitable') .css({ margin: '1.4em 1em', width: 'auto' }) .append( $dlgtbody = $('<tbody>').append( // header row $('<tr>').append( $('<th>') // top-left cell .css('width', '5%'), $('<th>') // value column header .css('width', '35%') .text(curpref.customListValueTitle ? curpref.customListValueTitle : 'Value'), $('<th>') // label column header .css('width', '60%') .text(curpref.customListLabelTitle ? curpref.customListLabelTitle : 'Label') ) ), $('<tfoot>').append( $('<tr>').append( $('<td>') .attr('colspan', '3') .append( $('<button>') .text('Add') .css('min-width', '8em') .attr('type', 'button') .on('click', () => { Twinkle.config.listDialog.addRow($dlgtbody); }) ) ) ) ), $('<button>') .text('Save changes') .attr('type', 'submit') // so Morebits.SimpleWindow puts the button in the button pane .on('click', () => { Twinkle.config.listDialog.save($prefbutton, $dlgtbody); dialog.close(); }), $('<button>') .text('Reset') .attr('type', 'submit') .on('click', () => { Twinkle.config.listDialog.reset($prefbutton, $dlgtbody); }), $('<button>') .text('Cancel') .attr('type', 'submit') .on('click', () => { dialog.close(); }) )[0] ); // content rows let gotRow = false; $.each(curvalue, (k, v) => { gotRow = true; Twinkle.config.listDialog.addRow($dlgtbody, v.value, v.label); }); // if there are no values present, add a blank row to start the user off if (!gotRow) { Twinkle.config.listDialog.addRow($dlgtbody); } dialog.display(); }; // Resets the data value, re-populates based on the new (default) value, then saves the // old data value again (less surprising behaviour) Twinkle.config.listDialog.reset = function twinkleconfigListDialogReset($button, $tbody) { // reset value on button const curpref = $button.data('pref'); const oldvalue = $button.data('value'); Twinkle.config.resetPref(curpref); // reset form $tbody.find('tr').slice(1).remove(); // all rows except the first (header) row // add the new values const curvalue = $button.data('value'); $.each(curvalue, (k, v) => { Twinkle.config.listDialog.addRow($tbody, v.value, v.label); }); // save the old value $button.data('value', oldvalue); }; Twinkle.config.listDialog.save = function twinkleconfigListDialogSave($button, $tbody) { const result = []; let current = {}; $tbody.find('input[type="text"]').each((inputkey, input) => { if ($(input).hasClass('twinkle-config-customlist-value')) { current = { value: input.value }; } else { current.label = input.value; // exclude totally empty rows if (current.value || current.label) { result.push(current); } } }); $button.data('value', result); }; // reset/restore defaults Twinkle.config.resetPrefLink = function twinkleconfigResetPrefLink(e) { const wantedpref = e.target.id.slice(21); // "twinkle-config-reset-" prefix is stripped // search tactics $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // continue: skip impossibilities } let foundit = false; $(section.preferences).each((prefkey, pref) => { if (pref.name !== wantedpref) { return true; // continue } Twinkle.config.resetPref(pref); foundit = true; return false; // break }); if (foundit) { return false; // break } }); return false; // stop link from scrolling page }; Twinkle.config.resetPref = function twinkleconfigResetPref(pref) { switch (pref.type) { case 'boolean': document.getElementById(pref.name).checked = Twinkle.defaultConfig[pref.name]; break; case 'string': case 'integer': case 'enum': document.getElementById(pref.name).value = Twinkle.defaultConfig[pref.name]; break; case 'set': $.each(pref.setValues, (itemkey) => { if (document.getElementById(pref.name + '_' + itemkey)) { document.getElementById(pref.name + '_' + itemkey).checked = Twinkle.defaultConfig[pref.name].includes(itemkey); } }); break; case 'customList': $(document.getElementById(pref.name)).data('value', Twinkle.defaultConfig[pref.name]); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } }; Twinkle.config.resetAllPrefs = function twinkleconfigResetAllPrefs() { // no confirmation message - the user can just refresh/close the page to abort $(Twinkle.config.sections).each((sectionkey, section) => { if (section.hidden || (section.adminOnly && !Morebits.userIsSysop)) { return true; // continue: skip impossibilities } $(section.preferences).each((prefkey, pref) => { if (!pref.adminOnly || Morebits.userIsSysop) { Twinkle.config.resetPref(pref); } }); return true; }); return false; // stop link from scrolling page }; Twinkle.config.save = function twinkleconfigSave(e) { Morebits.Status.init(document.getElementById('twinkle-config-content')); const userjs = mw.config.get('wgFormattedNamespaces')[mw.config.get('wgNamespaceIds').user] + ':' + mw.config.get('wgUserName') + '/twinkleoptions.js'; const wikipediaPage = new Morebits.wiki.Page(userjs, 'Saving preferences to ' + userjs); wikipediaPage.setCallbackParameters(e.target); wikipediaPage.load(Twinkle.config.writePrefs); return false; }; Twinkle.config.writePrefs = function twinkleconfigWritePrefs(pageobj) { const form = pageobj.getCallbackParameters(); // this is the object which gets serialized into JSON; only // preferences that this script knows about are kept const newConfig = {optionsVersion: 2.1}; // a comparison function is needed later on // it is just enough for our purposes (i.e. comparing strings, numbers, booleans, // arrays of strings, and arrays of { value, label }) // and it is not very robust: e.g. compare([2], ["2"]) === true, and // compare({}, {}) === false, but it's good enough for our purposes here const compare = function(a, b) { if (Array.isArray(a)) { if (a.length !== b.length) { return false; } const asort = a.sort(), bsort = b.sort(); for (let i = 0; asort[i]; ++i) { // comparison of the two properties of custom lists if ((typeof asort[i] === 'object') && (asort[i].label !== bsort[i].label || asort[i].value !== bsort[i].value)) { return false; } else if (asort[i].toString() !== bsort[i].toString()) { return false; } } return true; } return a === b; }; $(Twinkle.config.sections).each((sectionkey, section) => { if (section.adminOnly && !Morebits.userIsSysop) { return; // i.e. "continue" in this context } // reach each of the preferences from the form $(section.preferences).each((prefkey, pref) => { let userValue; // = undefined // only read form values for those prefs that have them if (!pref.adminOnly || Morebits.userIsSysop) { if (!section.hidden) { switch (pref.type) { case 'boolean': // read from the checkbox userValue = form[pref.name].checked; break; case 'string': // read from the input box or combo box case 'enum': userValue = form[pref.name].value; break; case 'integer': // read from the input box userValue = parseInt(form[pref.name].value, 10); if (isNaN(userValue)) { Morebits.Status.warn('Saving', 'The value you specified for ' + pref.name + ' (' + pref.value + ') was invalid. The save will continue, but the invalid data value will be skipped.'); userValue = null; } break; case 'set': // read from the set of check boxes userValue = []; if (pref.setDisplayOrder) { // read only those keys specified in the display order $.each(pref.setDisplayOrder, (itemkey, item) => { if (form[pref.name + '_' + item].checked) { userValue.push(item); } }); } else { // read all the keys in the list of values $.each(pref.setValues, (itemkey) => { if (form[pref.name + '_' + itemkey].checked) { userValue.push(itemkey); } }); } break; case 'customList': // read from the jQuery data stored on the button object userValue = $(form[pref.name]).data('value'); break; default: alert('twinkleconfig: unknown data type for preference ' + pref.name); break; } } else if (Twinkle.prefs) { // Retain the hidden preferences that may have customised by the user from twinkleoptions.js // undefined if not set userValue = Twinkle.prefs[pref.name]; } } // only save those preferences that are *different* from the default if (userValue !== undefined && !compare(userValue, Twinkle.defaultConfig[pref.name])) { newConfig[pref.name] = userValue; } }); }); let text = '// twinkleoptions.js: personal Twinkle preferences file\n' + '//\n' + '// NOTE: The easiest way to change your Twinkle preferences is by using the\n' + '// Twinkle preferences panel, at [[' + Morebits.pageNameNorm + ']].\n' + '//\n' + '// This file is AUTOMATICALLY GENERATED. Any changes you make (aside from\n' + '// changing the configuration parameters in a valid-JavaScript way) will be\n' + '// overwritten the next time you click "save" in the Twinkle preferences\n' + '// panel. If modifying this file, make sure to use correct JavaScript.\n' + // eslint-disable-next-line no-useless-concat '// <no' + 'wiki>\n' + '\n' + 'window.Twinkle.prefs = '; text += JSON.stringify(newConfig, null, 2); text += ';\n' + '\n' + // eslint-disable-next-line no-useless-concat '// </no' + 'wiki>\n' + '// End of twinkleoptions.js\n'; pageobj.setPageText(text); pageobj.setEditSummary('Saving Twinkle preferences: automatic edit from [[:' + Morebits.pageNameNorm + ']]'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('recreate'); pageobj.save(Twinkle.config.saveSuccess); }; Twinkle.config.saveSuccess = function twinkleconfigSaveSuccess(pageobj) { pageobj.getStatusElement().info('successful'); const noticebox = document.createElement('div'); noticebox.className = 'cdx-message cdx-message--success'; noticebox.style.fontSize = '100%'; noticebox.innerHTML = '<p><b>Your Twinkle preferences have been saved.</b> To see the changes, you will need to clear your browser cache entirely (see <a href="' + mw.util.getUrl('WP:BYPASS') + '" title="WP:BYPASS">WP:BYPASS</a> for instructions).</p>'; mw.loader.using('mediawiki.htmlform.codex.styles', () => { Morebits.Status.root.appendChild(noticebox); }); const noticeclear = document.createElement('br'); noticeclear.style.clear = 'both'; Morebits.Status.root.appendChild(noticeclear); }; Twinkle.addInitCallback(Twinkle.config.init); }()); // </nowiki> 7426vn5lknuhay1my9oic5zdd6m4oaw ಮಾದ್ಯಮೊವಿಕಿ:Gadget-twinklexfd.js 8 25402 365977 355531 2026-07-12T03:17:15Z en>SD0001 0 Repo at db09b35: Parsoid section wrapper compatibility (#2388) 365977 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinklexfd.js: XFD module **************************************** * Mode of invocation: Tab ("XFD") * Active on: Existing, non-special pages, except for file pages with no local (non-Commons) file which are not redirects */ Twinkle.xfd = function twinklexfd() { // Disable on: // * special pages // * non-existent pages // * files on Commons, whether there is a local page or not (unneeded local pages of files on Commons are eligible for CSD F2, or R4 if it's a redirect) if (mw.config.get('wgNamespaceNumber') < 0 || !mw.config.get('wgArticleId') || (mw.config.get('wgNamespaceNumber') === 6 && document.getElementById('mw-sharedupload'))) { return; } let tooltip = 'Start a discussion for deleting'; if (mw.config.get('wgIsRedirect')) { tooltip += ' or retargeting this redirect'; } else { switch (mw.config.get('wgNamespaceNumber')) { case 0: tooltip += ' or moving this article'; break; case 10: tooltip += ' or merging this template'; break; case 828: tooltip += ' or merging this module'; break; case 6: tooltip += ' this file'; break; case 14: tooltip += ', merging or renaming this category'; break; default: tooltip += ' this page'; break; } } Twinkle.addPortletLink(Twinkle.xfd.callback, 'XFD', 'tw-xfd', tooltip); }; const utils = { /** Get ordinal number figure */ num2order: function(num) { switch (num) { case 1: return ''; case 2: return '2nd'; case 3: return '3rd'; default: return num + 'th'; } }, /** * Remove namespace name from title if present * Exception-safe wrapper around mw.Title * * @param {string} title */ stripNs: function(title) { const title_obj = mw.Title.newFromUserInput(title); if (!title_obj) { return title; // user entered invalid input; do nothing } return title_obj.getMainText(); }, /** * Add namespace name to page title if not already given * CAUTION: namespace name won't be added if a namespace (*not* necessarily * the same as the one given) already is there in the title * * @param {string} title * @param {number} namespaceNumber */ addNs: function(title, namespaceNumber) { const title_obj = mw.Title.newFromUserInput(title, namespaceNumber); if (!title_obj) { return title; // user entered invalid input; do nothing } return title_obj.toText(); }, /** * Provide Wikipedian TLA style: AfD, RfD, CfDS, RM, SfD, etc. * * @param {string} venue * @return {string} */ toTLACase: function(venue) { return venue .toString() // Everybody up, inclduing rm and the terminal s in cfds .toUpperCase() // Lowercase the central f in a given TLA and normalize sfd-t and sfr-t .replace(/(.)F(.)(?:-.)?/, '$1f$2'); } }; Twinkle.xfd.currentRationale = null; // error callback on Morebits.Status.object Twinkle.xfd.printRationale = function twinklexfdPrintRationale() { if (Twinkle.xfd.currentRationale) { Morebits.Status.printUserText(Twinkle.xfd.currentRationale, 'Your deletion rationale is provided below, which you can copy and paste into a new XFD dialog if you wish to try again:'); // only need to print the rationale once Twinkle.xfd.currentRationale = null; } }; Twinkle.xfd.callback = function twinklexfdCallback() { const Window = new Morebits.SimpleWindow(700, 400); Window.setTitle('Start a deletion discussion (XfD)'); Window.setScriptName('Twinkle'); Window.addFooterLink('About deletion discussions', 'WP:XFD'); Window.addFooterLink('XfD prefs', 'WP:TW/PREF#xfd'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#xfd'); Window.addFooterLink('Give feedback', 'WT:TW'); const form = new Morebits.QuickForm(Twinkle.xfd.callback.evaluate); const categories = form.append({ type: 'select', name: 'venue', label: 'Deletion discussion venue:', tooltip: 'When activated, a default choice is made, based on what namespace you are in. This default should be the most appropriate.', event: Twinkle.xfd.callback.change_category }); const namespace = mw.config.get('wgNamespaceNumber'); categories.append({ type: 'option', label: 'AfD (Articles for deletion)', selected: namespace === 0, // Main namespace value: 'afd' }); categories.append({ type: 'option', label: 'TfD (Templates for discussion)', selected: [10, 828].includes(namespace), // Template and module namespaces value: 'tfd' }); categories.append({ type: 'option', label: 'FfD (Files for discussion)', selected: namespace === 6, // File namespace value: 'ffd' }); categories.append({ type: 'option', label: 'CfD (Categories for discussion)', selected: namespace === 14 || (namespace === 10 && /-stub$/.test(Morebits.pageNameNorm)), // Category namespace and stub templates value: 'cfd' }); categories.append({ type: 'option', label: 'CfD/S (Categories for speedy renaming)', value: 'cfds' }); categories.append({ type: 'option', label: 'MfD (Miscellany for deletion)', selected: ![0, 6, 10, 14, 828].includes(namespace) || Morebits.pageNameNorm.indexOf('Template:User ', 0) === 0, // Other namespaces, and userboxes in template namespace value: 'mfd' }); categories.append({ type: 'option', label: 'RfD (Redirects for discussion)', selected: mw.config.get('wgIsRedirect'), value: 'rfd' }); categories.append({ type: 'option', label: 'RM (Requested moves)', selected: false, value: 'rm' }); form.append({ type: 'div', id: 'wrong-venue-warn', style: 'color: red; font-style: italic' }); form.append({ type: 'checkbox', list: [ { label: 'Notify page creator if possible', value: 'notify', name: 'notifycreator', tooltip: "A notification template will be placed on the creator's talk page if this is true.", checked: true } ] }); form.append({ type: 'field', label: 'Work area', name: 'work_area' }); const previewlink = document.createElement('a'); $(previewlink).on('click', () => { Twinkle.xfd.callbacks.preview(result); // |result| is defined below }); previewlink.style.cursor = 'pointer'; previewlink.textContent = 'Preview'; form.append({ type: 'div', id: 'xfdpreview', label: [previewlink] }); form.append({ type: 'div', id: 'twinklexfd-previewbox', style: 'display: none' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.previewer = new Morebits.wiki.Preview($(result).find('div#twinklexfd-previewbox').last()[0]); // We must init the controls const evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.venue.dispatchEvent(evt); }; Twinkle.xfd.callback.wrongVenueWarning = function twinklexfdWrongVenueWarning(venue) { let text = ''; const namespace = mw.config.get('wgNamespaceNumber'); switch (venue) { case 'afd': if (namespace !== 0) { text = 'AfD is generally appropriate only for articles.'; } else if (mw.config.get('wgIsRedirect')) { text = 'Please use RfD for redirects.'; } break; case 'tfd': if (namespace === 10 && /-stub$/.test(Morebits.pageNameNorm)) { text = 'Use CfD for stub templates.'; } else if (Morebits.pageNameNorm.indexOf('Template:User ') === 0) { text = 'Please use MfD for userboxes'; } break; case 'cfd': if (![10, 14].includes(namespace)) { text = 'CfD is only for categories and stub templates.'; } break; case 'cfds': if (namespace !== 14) { text = 'CfDS is only for categories.'; } break; case 'ffd': if (namespace !== 6) { text = 'FFD is selected but this page doesn\'t look like a file!'; } break; case 'rm': if (namespace === 14) { // category text = 'Please use CfD or CfDS for category renames.'; } else if ([118, 119, 2, 3].includes(namespace)) { // draft, draft talk, user, user talk text = 'RMs are not permitted in draft and userspace, unless they are uncontroversial technical requests.'; } break; default: // mfd or rfd break; } $('#wrong-venue-warn').text(text); }; Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory(e) { const value = e.target.value; const form = e.target.form; const old_area = Morebits.QuickForm.getElements(e.target.form, 'work_area')[0]; let work_area = null; const oldreasontextbox = form.getElementsByTagName('textarea')[0]; const oldreason = oldreasontextbox ? oldreasontextbox.value : ''; const appendReasonBox = function twinklexfdAppendReasonBox() { work_area.append({ type: 'textarea', name: 'reason', label: 'Reason:', value: oldreason, tooltip: 'You can use wikimarkup in your reason. Twinkle will automatically sign your post.' }); }; Twinkle.xfd.callback.wrongVenueWarning(value); form.previewer.closePreview(); switch (value) { case 'afd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Articles for deletion', name: 'work_area' }); work_area.append({ type: 'div', label: '', // Added later by Twinkle.makeFindSourcesDiv() id: 'twinkle-xfd-findsources', style: 'margin-bottom: 5px; margin-top: -5px;' }); work_area.append({ type: 'select', name: 'outcome', label: 'Desired outcome:', event: Twinkle.xfd.callbacks.changeAfdOutcome, list: [ { label: 'Delete', value: 'deletion' }, { label: 'Merge', value: 'merging' }, { label: 'Redirect', value: 'redirecting' }, { label: 'Draftify', value: 'draftification' } ] }); work_area.append({ name: 'afdtarget', type: 'input', label: 'Target page:', tooltip: 'Target page for the redirect or merge.', event: Twinkle.xfd.callbacks.changeAfdTarget }); work_area.append({ type: 'checkbox', list: [ { label: 'Wrap deletion tag with &lt;noinclude&gt;', value: 'noinclude', name: 'noinclude', tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t transclude. This option is not normally required.' } ] }); work_area.append({ type: 'select', name: 'xfdcat', label: 'Choose what category this nomination belongs in:', list: [ { label: 'Unknown', value: '?', selected: true }, { label: 'Media and music', value: 'M' }, { label: 'Organisation, corporation, or product', value: 'O' }, { label: 'Biographical', value: 'B' }, { label: 'Society topics', value: 'S' }, { label: 'Web or internet', value: 'W' }, { label: 'Games or sports', value: 'G' }, { label: 'Science and technology', value: 'T' }, { label: 'Fiction and the arts', value: 'F' }, { label: 'Places and transportation', value: 'P' }, { label: 'Indiscernible or unclassifiable topic', value: 'I' }, { label: 'Debate not yet sorted', value: 'U' } ] }); work_area.append({ type: 'select', multiple: true, name: 'delsortCats', label: 'Choose deletion sorting categories:', tooltip: 'Select a few categories that are specifically relevant to the subject of the article. Be as precise as possible; categories like People and USA should only be used when no other categories apply.' }); // grab deletion sort categories from en-wiki Morebits.wiki.getCachedJson('Wikipedia:WikiProject_Deletion_sorting/Computer-readable.json').then((delsortCategories) => { const $select = $('[name="delsortCats"]'); $.each(delsortCategories, (groupname, list) => { const $optgroup = $('<optgroup>').attr('label', groupname); const $delsortCat = $select.append($optgroup); list.forEach((item) => { const $option = $('<option>').val(item).text(item); $delsortCat.append($option); }); }); }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); // Now that we've rendered the form, hide the target text box. Unhide it later for certain outcomes, using a callback. $('[name="afdtarget"]').parent().hide(); Twinkle.makeFindSourcesDiv('#twinkle-xfd-findsources'); $(work_area).find('[name=delsortCats]') .attr('data-placeholder', 'Select delsort pages') .select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matcher, templateResult: Morebits.select2.highlightSearchMatches, language: { searching: Morebits.select2.queryInterceptor }, // Link text to the page itself templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl('Wikipedia:WikiProject_Deletion_sorting/' + choice.text), target: '_blank' }); } }); mw.util.addCSS( // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + '.select2-results .select2-results__group { padding-top: 1px; padding-bottom: 1px; } ' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); break; case 'tfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Templates for discussion', name: 'work_area' }); var templateOrModule = mw.config.get('wgPageContentModel') === 'Scribunto' ? 'module' : 'template'; work_area.append({ type: 'select', label: 'Choose type of action wanted:', name: 'xfdcat', event: function(e) { const target = e.target; let tfdtarget = target.form.tfdtarget; // add/remove extra input box if (target.value === 'tfm' && !tfdtarget) { tfdtarget = new Morebits.QuickForm.Element({ name: 'tfdtarget', type: 'input', label: 'Other ' + templateOrModule + ' to be merged:', tooltip: 'Required. Should not include the ' + Morebits.string.toUpperCaseFirstChar(templateOrModule) + ': namespace prefix.', required: true }); target.parentNode.appendChild(tfdtarget.render()); } else { $(Morebits.QuickForm.getElementContainer(tfdtarget)).remove(); tfdtarget = null; } }, list: [ { label: 'Deletion', value: 'tfd', selected: true }, { label: 'Merge', value: 'tfm' } ] }); work_area.append({ type: 'select', name: 'templatetype', label: 'Deletion tag display style:', tooltip: 'Which <code>type=</code> parameter to pass to the TfD tag template.', list: templateOrModule === 'module' ? [ { value: 'module', label: 'Module', selected: true } ] : [ { value: 'standard', label: 'Standard', selected: true }, { value: 'sidebar', label: 'Sidebar/infobox', selected: $('.infobox').length }, { value: 'inline', label: 'Inline template', selected: (() => { // TODO: clean up after legacy parser is permanently replaced with Parsoid const $oldParser = $('.mw-parser-output > p .Inline-Template'); const $newParser = $('.mw-parser-output section[data-mw-section-id="0"] p .Inline-Template'); const $scanTarget = $newParser.length ? $newParser : $oldParser; return $scanTarget.length; })() }, { value: 'tiny', label: 'Tiny inline' }, { value: 'disabled', label: 'Disabled' } ] }); work_area.append({ type: 'checkbox', list: [ { label: 'Wrap deletion tag with &lt;noinclude&gt; (for substituted templates only)', value: 'noinclude', name: 'noinclude', tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t get substituted along with the template.', disabled: templateOrModule === 'module', checked: !!$('.box-Subst_only').length // Default to checked if page carries {{subst only}} } ] }); work_area.append({ type: 'checkbox', list: [ { label: 'Notify talk pages of affected user scripts', value: 'devpages', name: 'devpages', tooltip: 'A notification will be sent to Twinkle, AWB, and Ultraviolet\'s talk pages if those user scripts are marked as using this template.', checked: true } ] }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'mfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Miscellany for deletion', name: 'work_area' }); if (mw.config.get('wgNamespaceNumber') !== 710) { // TimedText cannot be tagged, so asking whether to noinclude the tag is pointless work_area.append({ type: 'checkbox', list: [ { label: 'Wrap deletion tag with &lt;noinclude&gt;', value: 'noinclude', name: 'noinclude', tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t transclude. Select this option for userboxes.' } ] }); } if ((mw.config.get('wgNamespaceNumber') === 2 /* User: */ || mw.config.get('wgNamespaceNumber') === 3 /* User talk: */) && mw.config.exists('wgRelevantUserName')) { work_area.append({ type: 'checkbox', list: [ { label: 'Notify owner of userspace (if they are not the page creator)', value: 'notifyuserspace', name: 'notifyuserspace', tooltip: 'If the user in whose userspace this page is located is not the page creator (for example, the page is a rescued article stored as a userspace draft), notify the userspace owner as well.', checked: true } ] }); } appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'ffd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Discussion venues for files', name: 'work_area' }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'cfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Categories for discussion', name: 'work_area' }); var isCategory = mw.config.get('wgNamespaceNumber') === 14; work_area.append({ type: 'select', label: 'Choose type of action wanted:', name: 'xfdcat', event: function(e) { const value = e.target.value, cfdtarget = e.target.form.cfdtarget; let cfdtarget2 = e.target.form.cfdtarget2; // update enabled status cfdtarget.disabled = value === 'cfd' || value === 'sfd-t'; if (isCategory) { // update label if (value === 'cfs') { Morebits.QuickForm.setElementLabel(cfdtarget, 'Target categories: '); } else if (value === 'cfc') { Morebits.QuickForm.setElementLabel(cfdtarget, 'Target article: '); } else { Morebits.QuickForm.setElementLabel(cfdtarget, 'Target category: '); } // add/remove extra input box if (value === 'cfs') { if (cfdtarget2) { cfdtarget2.disabled = false; $(cfdtarget2).show(); } else { cfdtarget2 = document.createElement('input'); cfdtarget2.setAttribute('name', 'cfdtarget2'); cfdtarget2.setAttribute('type', 'text'); cfdtarget2.setAttribute('required', 'true'); cfdtarget.parentNode.appendChild(cfdtarget2); } } else { $(cfdtarget2).prop('disabled', true); $(cfdtarget2).hide(); } } else { // Update stub template label Morebits.QuickForm.setElementLabel(cfdtarget, 'Target stub template: '); } }, list: isCategory ? [ { label: 'Deletion', value: 'cfd', selected: true }, { label: 'Merge', value: 'cfm' }, { label: 'Renaming', value: 'cfr' }, { label: 'Split', value: 'cfs' }, { label: 'Convert into article', value: 'cfc' } ] : [ { label: 'Stub Deletion', value: 'sfd-t', selected: true }, { label: 'Stub Renaming', value: 'sfr-t' } ] }); work_area.append({ type: 'input', name: 'cfdtarget', label: 'Target category:', // default, changed above disabled: true, required: true, // only when enabled value: '' }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'cfds': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Categories for speedy renaming', name: 'work_area' }); work_area.append({ type: 'select', label: 'C2 sub-criterion:', name: 'xfdcat', tooltip: 'See WP:CFDS for full explanations.', list: [ { label: 'C2A: Typographic and spelling fixes', value: 'C2A', selected: true }, { label: 'C2B: Naming conventions and disambiguation', value: 'C2B' }, { label: 'C2C: Consistency with names of similar categories', value: 'C2C' }, { label: 'C2D: Rename to match article name', value: 'C2D' }, { label: 'C2E: Author request', value: 'C2E' }, { label: 'C2F: One eponymous article', value: 'C2F' } ] }); work_area.append({ type: 'input', name: 'cfdstarget', label: 'New name:', size: 70, value: '', required: true }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'rfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Redirects for discussion', name: 'work_area' }); work_area.append({ type: 'checkbox', list: [ { label: 'Notify target page if possible', value: 'relatedpage', name: 'relatedpage', tooltip: "A notification template will be placed on the talk page of this redirect's target if this is true.", checked: true } ] }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'rm': { work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Requested moves', name: 'work_area' }); work_area.append({ type: 'checkbox', list: [ { label: 'Uncontroversial technical request', value: 'rmtr', name: 'rmtr', tooltip: 'Use this option when you are unable to perform this uncontroversial move yourself because of a technical reason (e.g. a page already exists at the new title, or the page is protected)', checked: false, event: function() { $('input[name="newname"]', form).prop('required', this.checked); $('input[type="button"][value="more"]', form)[0].sublist.inputs[1].required = this.checked; }, subgroup: { type: 'checkbox', list: [ { label: 'Opt out of discussion if the request is contested', value: 'rmtr-discuss', name: 'rmtr-discuss', tooltip: 'Use this option if you prefer to withdraw the request if contested, rather than discuss it. This suppresses the "discuss" link, which may be used to convert your request to a discussion on the talk page.', checked: false } ] } } ] }); work_area.append({ type: 'dyninput', inputs: [ { label: 'From:', name: 'currentname', required: true }, { label: 'To:', name: 'newname', tooltip: 'Required for technical requests. Otherwise, if unsure of the appropriate title, you may leave it blank.' } ], min: 1 }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); const currentNonTalkPage = mw.Title.newFromText(Morebits.pageNameNorm).getSubjectPage().toText(); form.currentname.value = currentNonTalkPage; break; } default: work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Nothing for anything', name: 'work_area' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; } // Return to checked state when switching, but no creator notification for CFDS or RM form.notifycreator.disabled = value === 'cfds' || value === 'rm'; form.notifycreator.checked = !form.notifycreator.disabled; }; Twinkle.xfd.callbacks = { /** If the user hasn't modified the reason much, modify the reason to include the target article. If the user has modified the reason box with a custom reason, do nothing, since we don't want to blank their work. */ changeAfdTarget: function() { const $afdTarget = $('[name="afdtarget"]'); const $reason = $('[name="reason"]'); const $outcome = $('[name="outcome"]'); if ($reason.val().endsWith('because ')) { // Target has something typed in if ($afdTarget.val()) { if ($outcome.val() === 'redirecting') { $reason.val(`I propose '''redirecting''' to [[${$afdTarget.val()}]] because `); } else if ($outcome.val() === 'merging') { $reason.val(`I propose '''merging''' to [[${$afdTarget.val()}]] because `); } // Target is blank } else { if ($outcome.val() === 'redirecting') { $reason.val("I propose '''redirecting''' because "); } else if ($outcome.val() === 'merging') { $reason.val("I propose '''merging''' because "); } } } }, /** Print a default reason in the reason textarea, depending on which outcome is selected from the outcome dropdown list. */ changeAfdOutcome: function() { const $outcome = $('[name="outcome"]'); const $reason = $('[name="reason"]'); const $afdTarget = $('[name="afdtarget"]'); $afdTarget.val(''); if ($outcome.val() === 'redirecting') { $reason.val("I propose '''redirecting''' because "); $afdTarget.parent().show(); } else if ($outcome.val() === 'merging') { $reason.val("I propose '''merging''' because "); $afdTarget.parent().show(); } else if ($outcome.val() === 'draftification') { $reason.val("I propose '''draftifying''' because "); $afdTarget.parent().hide(); } else if ($outcome.val() === 'deletion') { $reason.val(''); $afdTarget.parent().hide(); } }, /** Requires having the tag text (params.tagText) set ahead of time */ autoEditRequest: function(pageobj, params) { const talkName = new mw.Title(pageobj.getPageName()).getTalkPage().toText(); if (talkName === pageobj.getPageName()) { pageobj.getStatusElement().error('Page protected and nowhere to add an edit request, aborting'); } else { pageobj.getStatusElement().warn('Page protected, requesting edit'); const editRequest = '{{subst:Xfd edit protected|page=' + pageobj.getPageName() + '|discussion=' + params.discussionpage + (params.venue === 'rfd' ? '|rfd=yes' : '') + '|tag=<nowiki>' + params.tagText + '\u003C/nowiki>}}'; // U+003C: < const talk_page = new Morebits.wiki.Page(talkName, 'Automatically posting edit request on talk page'); talk_page.setNewSectionTitle('Edit request to complete ' + utils.toTLACase(params.venue) + ' nomination'); talk_page.setNewSectionText(editRequest); talk_page.setCreateOption('recreate'); talk_page.setWatchlist(Twinkle.getPref('xfdWatchPage')); talk_page.setFollowRedirect(true); // should never be needed, but if the article is moved, we would want to follow the redirect talk_page.setChangeTags(Twinkle.changeTags); talk_page.setCallbackParameters(params); talk_page.newSection(null, () => { talk_page.getStatusElement().warn('Unable to add edit request, the talk page may be protected'); }); } }, getDiscussionWikitext: function(venue, params) { if (venue === 'cfds') { // CfD/S takes a completely different style return '* [[:' + Morebits.pageNameNorm + ']] to [[:' + params.cfdstarget + ']]\u00A0\u2013 ' + params.xfdcat + (params.reason ? ': ' + Morebits.string.formatReasonText(params.reason) : '.') + ' ~~~~'; // U+00A0 NO-BREAK SPACE; U+2013 EN RULE } if (venue === 'rm') { if (params.rmtr) { const rmtrDiscuss = params['rmtr-discuss'] ? '|discuss=no' : ''; return params.currentname .map((currentname, i) => `{{subst:RMassist|1=${currentname}|2=${params.newname[i]}${rmtrDiscuss}|reason=${params.reason}}}`) .join('\n'); } return `{{subst:Requested move${ params.currentname .map((currentname, i) => `|current${i + 1}=${currentname}|new${i + 1}=${params.newname[i]}`) .join('') }|reason=${params.reason}}}`; } let text = '{{subst:' + venue + '2'; const reasonKey = venue === 'ffd' ? 'Reason' : 'text'; // Add a reason unconditionally, so that at least a signature is added text += '|' + reasonKey + '=' + Morebits.string.formatReasonText(params.reason, true); if (venue === 'afd' || venue === 'mfd') { text += '|pg=' + Morebits.pageNameNorm; if (venue === 'afd') { text += '|cat=' + params.xfdcat; } } else if (venue === 'rfd') { text += '|redirect=' + Morebits.pageNameNorm; } else { text += '|1=' + mw.config.get('wgTitle'); if (mw.config.get('wgPageContentModel') === 'Scribunto') { text += '|module=Module:'; } } if (params.rfdtarget) { text += '|target=' + params.rfdtarget + (params.section ? '#' + params.section : ''); } else if (params.tfdtarget) { text += '|2=' + params.tfdtarget; } else if (params.cfdtarget) { text += '|2=' + params.cfdtarget; if (params.cfdtarget2) { text += '|3=' + params.cfdtarget2; } } else if (params.uploader) { text += '|Uploader=' + params.uploader; } text += '}}'; if (venue === 'rfd' || venue === 'tfd' || venue === 'cfd') { text += '\n'; } // Don't delsort if delsortCats is undefined (TFD, FFD, etc.) // Don't delsort if delsortCats is an empty array (AFD where user chose no categories) if (Array.isArray(params.delsortCats) && params.delsortCats.length) { text += '\n{{subst:Deletion sorting/multi|' + params.delsortCats.join('|') + '|sig=~~~~}}'; } return text; }, showPreview: function(form, venue, params) { const templatetext = Twinkle.xfd.callbacks.getDiscussionWikitext(venue, params); if (venue === 'rm') { // RM templates are sensitive to page title form.previewer.beginRender(templatetext, params.rmtr ? 'Wikipedia:Requested moves/Technical requests' : new mw.Title(Morebits.pageNameNorm).getTalkPage().toText()); } else { form.previewer.beginRender(templatetext, 'WP:TW'); // Force wikitext } }, preview: function(form) { // venue, reason, xfdcat, tfdtarget, cfdtarget, cfdtarget2, cfdstarget, delsortCats, newname const params = Morebits.QuickForm.getInputData(form); const venue = params.venue; // Remove CfD or TfD namespace prefixes if given if (params.tfdtarget) { params.tfdtarget = utils.stripNs(params.tfdtarget); } else if (params.cfdtarget) { params.cfdtarget = utils.stripNs(params.cfdtarget); if (params.cfdtarget2) { params.cfdtarget2 = utils.stripNs(params.cfdtarget2); } } else if (params.cfdstarget) { // Add namespace if not given (CFDS) params.cfdstarget = utils.addNs(params.cfdstarget, 14); } if (venue === 'ffd') { // Fetch the uploader const page = new Morebits.wiki.Page(mw.config.get('wgPageName')); page.lookupCreation(() => { params.uploader = page.getCreator(); Twinkle.xfd.callbacks.showPreview(form, venue, params); }); } else if (venue === 'rfd') { // Find the target Twinkle.xfd.callbacks.rfd.findTarget(params, (params) => { Twinkle.xfd.callbacks.showPreview(form, venue, params); }); } else if (venue === 'cfd') { // Swap in CfD subactions Twinkle.xfd.callbacks.showPreview(form, params.xfdcat, params); } else { Twinkle.xfd.callbacks.showPreview(form, venue, params); } }, /** * Unified handler for sending {{Xfd notice}} notifications * Also handles userspace logging * * @param {Object} params * @param {string} notifyTarget The user or page being notified * @param {boolean} [noLog=false] Whether to skip logging to userspace * XfD log, especially useful in cases in where multiple notifications * may be sent out (MfD, TfM, RfD) * @param {string} [actionName] Alternative description of the action * being undertaken. Required if not notifying a user talk page. */ notifyUser: function(params, notifyTarget, noLog, actionName) { // Ensure items with User talk or no namespace prefix both end // up at user talkspace as expected, but retain the // prefix-less username for addToLog const userTalkNamespace = 3; notifyTarget = mw.Title.newFromText(notifyTarget, userTalkNamespace); const targetNS = notifyTarget.getNamespaceId(); const usernameOrTarget = notifyTarget.getRelativeText(userTalkNamespace); notifyTarget = notifyTarget.toText(); if (targetNS === userTalkNamespace) { // Disallow warning yourself if (usernameOrTarget === mw.config.get('wgUserName')) { Morebits.Status.warn('You (' + usernameOrTarget + ') created this page; skipping user notification'); // if we thought we would notify someone but didn't, // then jump to logging. Twinkle.xfd.callbacks.addToLog(params, null); return; } // Default is notifying the initial contributor, but MfD also // notifies userspace page owner actionName = actionName || 'Notifying initial contributor (' + usernameOrTarget + ')'; } const notifyText = Twinkle.xfd.callbacks.generateUserTalkNoticeWikitext(params); // Link to the venue; object used here rather than repetitive items in switch const venueNames = { afd: 'Articles for deletion', tfd: 'Templates for discussion', mfd: 'Miscellany for deletion', cfd: 'Categories for discussion', ffd: 'Files for discussion', rfd: 'Redirects for discussion' }; const editSummary = 'Notification: [[' + params.discussionpage + '|listing]] of [[:' + Morebits.pageNameNorm + ']] at [[WP:' + venueNames[params.venue] + ']].'; const usertalkpage = new Morebits.wiki.Page(notifyTarget, actionName); usertalkpage.setAppendText(notifyText); usertalkpage.setEditSummary(editSummary); usertalkpage.setChangeTags(Twinkle.changeTags); usertalkpage.setCreateOption('recreate'); // Different pref for RfD target notifications if (params.venue === 'rfd' && targetNS !== 3) { usertalkpage.setWatchlist(Twinkle.getPref('xfdWatchRelated')); } else { usertalkpage.setWatchlist(Twinkle.getPref('xfdWatchUser')); } usertalkpage.setFollowRedirect(true, false); if (noLog) { usertalkpage.append(); } else { usertalkpage.append(() => { // Don't treat RfD target or MfD userspace owner as initialContrib in log if (!params.notifycreator) { notifyTarget = null; } // add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, usernameOrTarget); }, () => { // if user could not be notified, log nomination without mentioning that notification was sent Twinkle.xfd.callbacks.addToLog(params, null); }); } }, generateUserTalkNoticeWikitext: function(params) { // For grep: Afd notice, Mfd notice, Tfd notice, Cfd notice, Ffd notice, Rfd notice let notifytext = '\n{{subst:' + params.venue + ' notice'; const templateNamespace = 10; // Venue-specific parameters switch (params.venue) { case 'afd': notifytext += params.outcome !== 'deletion' ? '|outcome=' + params.outcome : ''; notifytext += params.afdtarget ? '|target=' + params.afdtarget : ''; // Tell the template to add " (Xnd nomination)" to the XFD title, if needed. // The &#32; (HTML space character) is needed to overcome MediaWiki's parameter auto-trim. notifytext += params.numbering !== '' ? '|order=&#32;' + params.numbering : ''; break; case 'mfd': // tell the template to add " (Xnd nomination)" to the XFD title, if needed notifytext += params.numbering !== '' ? '|order=&#32;' + params.numbering : ''; break; case 'tfd': if (params.xfdcat === 'tfm') { notifytext = '\n{{subst:Tfm notice|2=' + params.tfdtarget; } break; case 'cfd': notifytext += '|action=' + params.action + (mw.config.get('wgNamespaceNumber') === templateNamespace ? '|stub=yes' : ''); break; default: // ffd, rfd break; } notifytext += '|1=' + Morebits.pageNameNorm + '}} ~~~~'; return notifytext; }, addToLog: function(params, initialContrib) { if (!Twinkle.getPref('logXfdNominations') || Twinkle.getPref('noLogOnXfdNomination').includes(params.venue)) { return; } const usl = new Morebits.UserspaceLogger(Twinkle.getPref('xfdLogPageName'));// , 'Adding entry to userspace log'); usl.initialText = "This is a log of all [[WP:XFD|deletion discussion]] nominations made by this user using [[WP:TW|Twinkle]]'s XfD module.\n\n" + 'If you no longer wish to keep this log, you can turn it off using the [[Wikipedia:Twinkle/Preferences|preferences panel]], and ' + 'nominate this page for speedy deletion under [[WP:CSD#U1|CSD U1]].' + (Morebits.userIsSysop ? '\n\nThis log does not track XfD-related deletions made using Twinkle.' : ''); let editsummary; if (params.discussionpage) { editsummary = 'Logging [[' + params.discussionpage + '|' + utils.toTLACase(params.venue) + ' nomination]] of [[:' + Morebits.pageNameNorm + ']].'; } else { editsummary = 'Logging ' + utils.toTLACase(params.venue) + ' nomination of [[:' + Morebits.pageNameNorm + ']].'; } // If a logged file is deleted but exists on commons, the wikilink will be blue, so provide a link to the log const fileLogLink = mw.config.get('wgNamespaceNumber') === 6 ? ' ([{{fullurl:Special:Log|page=' + mw.util.wikiUrlencode(mw.config.get('wgPageName')) + '}} log])' : ''; // CFD/S and RM don't have canonical links const nominatedLink = params.discussionpage ? '[[' + params.discussionpage + '|nominated]]' : 'nominated'; let appendText = '# [[:' + Morebits.pageNameNorm + ']]:' + fileLogLink + ' ' + nominatedLink + ' at [[WP:' + params.venue.toUpperCase() + '|' + utils.toTLACase(params.venue) + ']]'; switch (params.venue) { case 'tfd': if (params.xfdcat === 'tfm') { appendText += ' (merge)'; if (params.tfdtarget) { const contentModel = mw.config.get('wgPageContentModel') === 'Scribunto' ? 'Module:' : 'Template:'; appendText += '; Other ' + contentModel.toLowerCase() + ' [['; if (!new RegExp('^:?' + Morebits.namespaceRegex([10, 828]) + ':', 'i').test(params.tfdtarget)) { appendText += contentModel; } appendText += params.tfdtarget + ']]'; } } break; case 'mfd': if (params.notifyuserspace && params.userspaceOwner && params.userspaceOwner !== initialContrib) { appendText += '; notified {{user|1=' + params.userspaceOwner + '}}'; } break; case 'cfd': appendText += ' (' + utils.toTLACase(params.xfdcat) + ')'; if (params.cfdtarget) { const categoryOrTemplate = params.xfdcat.charAt(0) === 's' ? 'Template:' : ':Category:'; appendText += '; ' + params.action + ' to [[' + categoryOrTemplate + params.cfdtarget + ']]'; if (params.xfdcat === 'cfs' && params.cfdtarget2) { appendText += ', [[' + categoryOrTemplate + params.cfdtarget2 + ']]'; } } break; case 'cfds': appendText += ' (' + utils.toTLACase(params.xfdcat) + ')'; // Ensure there's more than just 'Category:' if (params.cfdstarget && params.cfdstarget.length > 9) { appendText += '; New name: [[:' + params.cfdstarget + ']]'; } break; case 'rfd': if (params.rfdtarget) { appendText += '; Target: [[:' + params.rfdtarget + ']]'; if (params.relatedpage) { appendText += ' (notified)'; } } break; case 'rm': appendText = params.currentname .map((currentname, i) => `# [[:${currentname}]]: ${nominatedLink} at [[WP:RM${params.rmtr ? '/TR' : ''}|]]${params.newname[i] ? `; New name: [[:${params.newname[i]}]]` : ''}`) .join('\n'); break; default: // afd or ffd break; } if (initialContrib && params.notifycreator) { appendText += '; notified {{user|1=' + initialContrib + '}}'; } appendText += ' ~~~~~'; if (params.reason) { appendText += "\n#* '''Reason''': " + Morebits.string.formatReasonForLog(params.reason); } usl.changeTags = Twinkle.changeTags; usl.log(appendText, editsummary); }, afd: { main: function(apiobj) { const response = apiobj.getResponse(); const titles = response.query.allpages; // There has been no earlier entries with this prefix, just go on. if (titles.length <= 0) { apiobj.params.numbering = apiobj.params.number = ''; } else { let number = 0; for (let i = 0; i < titles.length; ++i) { const title = titles[i].title; // First, simple test, is there an instance with this exact name? if (title === 'Wikipedia:Articles for deletion/' + Morebits.pageNameNorm) { number = Math.max(number, 1); continue; } const order_re = new RegExp('^' + Morebits.string.escapeRegExp('Wikipedia:Articles for deletion/' + Morebits.pageNameNorm) + '\\s*\\(\\s*(\\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\\s*\\)\\s*$'); const match = order_re.exec(title); // No match; A non-good value // Or the match is an unrealistically high number. Avoid false positives such as Wikipedia:Articles for deletion/The Basement (2014), by ignoring matches greater than 100 if (!match || match[1] > 100) { continue; } // A match, set number to the max of current number = Math.max(number, Number(match[1])); } apiobj.params.number = utils.num2order(parseInt(number, 10) + 1); apiobj.params.numbering = number > 0 ? ' (' + apiobj.params.number + ' nomination)' : ''; } apiobj.params.discussionpage = 'Wikipedia:Articles for deletion/' + Morebits.pageNameNorm + apiobj.params.numbering; Morebits.Status.info('Next discussion page', '[[' + apiobj.params.discussionpage + ']]'); // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = apiobj.params.discussionpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Tagging article const wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Adding deletion tag to article'); wikipedia_page.setFollowRedirect(true); // should never be needed, but if the article is moved, we would want to follow the redirect wikipedia_page.setChangeTags(Twinkle.changeTags); // Here to apply to triage wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.afd.taggingArticle); }, // Tagging needs to happen before everything else: this means we can check if there is an AfD tag already on the page taggingArticle: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); if (!pageobj.exists()) { statelem.error("It seems that the page doesn't exist; perhaps it has already been deleted"); return; } // Check for existing AfD tag, for the benefit of new page patrollers const textNoAfd = text.replace(/<!--.*AfD.*\n\{\{(?:Article for deletion\/dated|AfDM).*\}\}\n<!--.*(?:\n<!--.*)?AfD.*(?:\s*\n)?/g, ''); if (text !== textNoAfd) { if (confirm('An AfD tag was found on this article. Maybe someone beat you to it. \nClick OK to replace the current AfD tag (not recommended), or Cancel to abandon your nomination.')) { text = textNoAfd; } else { statelem.error('Article already tagged with AfD tag, and you chose to abort'); window.location.reload(); return; } } // Now we know we want to go ahead with it, trigger the other AJAX requests // Mark the page as curated/patrolled, if wanted if (Twinkle.getPref('markXfdPagesAsPatrolled')) { pageobj.triage(); } // Start discussion page, will also handle pagetriage and delsort listings let wikipedia_page = new Morebits.wiki.Page(params.discussionpage, 'Creating article deletion discussion page'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.afd.discussionPage); // Today's list const date = new Morebits.Date(pageobj.getLoadTime()); wikipedia_page = new Morebits.wiki.Page('Wikipedia:Articles for deletion/Log/' + date.format('YYYY MMMM D', 'utc'), "Adding discussion to today's list"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.afd.todaysList); // Notification to first contributor if (params.notifycreator) { const thispage = new Morebits.wiki.Page(mw.config.get('wgPageName')); thispage.setCallbackParameters(params); thispage.setLookupNonRedirectCreator(true); // Look for author of first non-redirect revision thispage.lookupCreation((pageobj) => { Twinkle.xfd.callbacks.notifyUser(pageobj.getCallbackParameters(), pageobj.getCreator()); }); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } // Add AFD tag to article params.tagText = Twinkle.xfd.callbacks.afd.generateArticleTagWikitext( params.noinclude, params.outcome, params.afdtarget, params.number ); // If the selected outcome is merge, add {{Merge from}} to the target page if (params.outcome === 'merging' && params.afdtarget) { wikipedia_page = new Morebits.wiki.Page(params.afdtarget, 'Tagging the target page with {{Merge from}}'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.xfd.callbacks.afd.tagTargetPageWithMergeFromTag); } if (pageobj.canEdit()) { // Remove some tags that should always be removed on AfD. text = text.replace(/\{\{\s*(dated prod|dated prod blp|Prod blp\/dated|Proposed deletion\/dated|prod2|Proposed deletion endorsed|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, ''); // Then, test if there are speedy deletion-related templates on the article. const textNoSd = text.replace(/\{\{\s*(db(-\w*)?|delete|(?:hang|hold)[- ]?on)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, ''); if (text !== textNoSd && confirm('A speedy deletion tag was found on this page. Should it be removed?')) { text = textNoSd; } // Insert tag after short description or any hatnotes const wikipage = new Morebits.wikitext.Page(text); text = wikipage.insertAfterTemplates(params.tagText, Twinkle.hatnoteRegex).getText(); pageobj.setPageText(text); pageobj.setEditSummary(`Nominated for ${params.outcome}; see [[:${params.discussionpage}]].`); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('nocreate'); pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, tagTargetPageWithMergeFromTag: function(pageobj) { const statelem = pageobj.getStatusElement(); if (!pageobj.exists()) { statelem.warn('Failed. Target page not found.'); return; } else if (!pageobj.canEdit()) { statelem.warn('Failed. Target page is protected from editing.'); return; } const params = pageobj.getCallbackParameters(); const tag = `{{Merge from |1=${Morebits.pageNameNorm} |target=${params.afdtarget} |afd=${Morebits.pageNameNorm + params.numbering} |date ={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}} }}`; const wikipage = new Morebits.wikitext.Page(pageobj.getPageText()); const text = wikipage.insertAfterTemplates(tag, Twinkle.hatnoteRegex).getText(); pageobj.setPageText(text); pageobj.setEditSummary('Nominated for merging; see [[:' + params.discussionpage + ']].'); pageobj.setCreateOption('nocreate'); pageobj.save(); }, generateArticleTagWikitext: function(noinclude, outcome, afdtarget, number) { let noIncludeStart = ''; let noIncludeEnd = ''; if (noinclude) { noIncludeStart = '<noinclude>'; noIncludeEnd = '</noinclude>'; } let templateAndParams = ''; const outcomeParam = outcome !== 'deletion' ? '|outcome=' + outcome : ''; const targetParam = afdtarget ? '|target=' + afdtarget : ''; const isFirstNomination = number === ''; if (isFirstNomination) { templateAndParams = 'subst:afd|help=off' + outcomeParam + targetParam; } else { templateAndParams = 'subst:afdx|' + number + '|help=off' + outcomeParam + targetParam; } return noIncludeStart + '{{' + templateAndParams + '}}' + noIncludeEnd + '\n'; }, discussionPage: function(pageobj) { const params = pageobj.getCallbackParameters(); pageobj.setPageText(Twinkle.xfd.callbacks.getDiscussionWikitext('afd', params)); pageobj.setEditSummary('Creating AfD discussion page for [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('createonly'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki // Actions that should wait on the discussion page actually being created // and whose errors shouldn't output the user rationale // List at deletion sorting pages if (params.delsortCats) { params.delsortCats.forEach((cat) => { const delsortPage = new Morebits.wiki.Page('Wikipedia:WikiProject Deletion sorting/' + cat, 'Adding to list of ' + cat + '-related deletion discussions'); delsortPage.setFollowRedirect(true); // In case a category gets renamed delsortPage.setCallbackParameters({discussionPage: params.discussionpage}); delsortPage.load(Twinkle.xfd.callbacks.afd.delsortListing); }); } }); }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = '{{subst:afd3|pg=' + Morebits.pageNameNorm + params.numbering + '}}\n'; let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:AfD log}}\n' + added_data; } else { const old_text = pageobj.getPageText() + '\n'; // MW strips trailing blanks, but we like them, so we add a fake one text = old_text.replace(/(<!-- Add new entries to the TOP of the following list -->\n+)/, '$1' + added_data); if (text === old_text) { const linknode = document.createElement('a'); linknode.setAttribute('href', mw.util.getUrl('Wikipedia:Twinkle/Fixing AFD') + '?action=purge'); linknode.appendChild(document.createTextNode('How to fix AFD')); statelem.error(['Could not find the target spot for the discussion. To fix this problem, please see ', linknode, '.']); return; } } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchList')); pageobj.setCreateOption('recreate'); pageobj.setDiscussionToolsAutoSubscribe(false); pageobj.save(); }, delsortListing: function(pageobj) { const discussionPage = pageobj.getCallbackParameters().discussionPage; const text = pageobj.getPageText().replace('directly below this line -->', 'directly below this line -->\n{{' + discussionPage + '}}'); pageobj.setPageText(text); pageobj.setEditSummary('Listing [[:' + discussionPage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('nocreate'); pageobj.setDiscussionToolsAutoSubscribe(false); pageobj.save(); } }, tfd: { main: function(pageobj) { const params = pageobj.getCallbackParameters(); const date = new Morebits.Date(pageobj.getLoadTime()); params.logpage = 'Wikipedia:Templates for discussion/Log/' + date.format('YYYY MMMM D', 'utc'); params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; // Add log/discussion page params to the already-loaded page object pageobj.setCallbackParameters(params); // Defined here rather than below to reduce duplication let watchModule, watch_query; if (params.scribunto) { const watchPref = Twinkle.getPref('xfdWatchPage'); // action=watch has no way to rely on user // preferences (T262912), so we do it manually. // The watchdefault pref appears to reliably return '1' (string), // but that's not consistent among prefs so might as well be "correct" watchModule = watchPref !== 'no' && (watchPref !== 'default' || !!parseInt(mw.user.options.get('watchdefault'), 10)); if (watchModule) { watch_query = { action: 'watch', titles: [mw.config.get('wgPageName')], token: mw.user.tokens.get('watchToken') }; // Only add the expiry if page is unwatched or already temporarily watched if (pageobj.getWatched() !== true && watchPref !== 'default' && watchPref !== 'yes') { watch_query.expiry = watchPref; } } } // Tagging template(s)/module(s) if (params.xfdcat === 'tfm') { // Merge let wikipedia_otherpage; if (params.scribunto) { wikipedia_otherpage = new Morebits.wiki.Page(params.otherTemplateName + '/doc', 'Tagging other module documentation with merge tag'); // Watch tagged module pages as well if (watchModule) { watch_query.titles.push(params.otherTemplateName); new Morebits.wiki.Api('Adding Modules to watchlist', watch_query).post(); } } else { wikipedia_otherpage = new Morebits.wiki.Page(params.otherTemplateName, 'Tagging other template with merge tag'); } // Tag this template/module Twinkle.xfd.callbacks.tfd.taggingTemplateForMerge(pageobj); // Tag other template/module wikipedia_otherpage.setFollowRedirect(true); const otherParams = $.extend({}, params); otherParams.otherTemplateName = Morebits.pageNameNorm; wikipedia_otherpage.setCallbackParameters(otherParams); wikipedia_otherpage.load(Twinkle.xfd.callbacks.tfd.taggingTemplateForMerge); } else { // delete if (params.scribunto && Twinkle.getPref('xfdWatchPage') !== 'no') { // Watch tagged module page as well if (watchModule) { new Morebits.wiki.Api('Adding Module to watchlist', watch_query).post(); } } Twinkle.xfd.callbacks.tfd.taggingTemplate(pageobj); } // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log"; // Adding discussion const wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's log"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.tfd.todaysList); // Notification to first contributors if (params.notifycreator) { const involvedpages = []; const seenusers = []; involvedpages.push(new Morebits.wiki.Page(mw.config.get('wgPageName'))); if (params.xfdcat === 'tfm') { if (params.scribunto) { involvedpages.push(new Morebits.wiki.Page('Module:' + params.tfdtarget)); } else { involvedpages.push(new Morebits.wiki.Page('Template:' + params.tfdtarget)); } } involvedpages.forEach((page) => { page.setCallbackParameters(params); page.lookupCreation((innerpage) => { const username = innerpage.getCreator(); if (!seenusers.includes(username)) { seenusers.push(username); // Only log once on merge nominations, for the initial template Twinkle.xfd.callbacks.notifyUser(innerpage.getCallbackParameters(), username, params.xfdcat === 'tfm' && innerpage.getPageName() !== Morebits.pageNameNorm); } }); }); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } // Notify developer(s) of script(s) that use(s) the nominated template if (params.devpages) { const inCategories = mw.config.get('wgCategories'); const categoryNotificationPageMap = { 'Templates used by Twinkle': 'Wikipedia talk:Twinkle', 'Templates used by AutoWikiBrowser': 'Wikipedia talk:AutoWikiBrowser', 'Templates used by Ultraviolet': 'Wikipedia talk:Ultraviolet' }; $.each(categoryNotificationPageMap, (category, page) => { if (inCategories.includes(category)) { Twinkle.xfd.callbacks.notifyUser(params, page, true, 'Notifying ' + page + ' of template nomination'); } }); } }, taggingTemplate: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{subst:template for discussion|help=off' + (params.templatetype !== 'standard' ? '|type=' + params.templatetype : '') + '}}'; if (pageobj.getContentModel() === 'sanitized-css') { params.tagText = '/* ' + params.tagText + ' */'; } else { if (params.noinclude) { params.tagText = '<noinclude>' + params.tagText + '</noinclude>'; } params.tagText += params.templatetype === 'standard' || params.templatetype === 'sidebar' || params.templatetype === 'disabled' ? '\n' : ''; // No newline for inline } if (pageobj.canEdit() && ['wikitext', 'sanitized-css'].includes(pageobj.getContentModel())) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Nominated for deletion; see [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); if (params.scribunto) { pageobj.setCreateOption('recreate'); // Module /doc might not exist } pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, taggingTemplateForMerge: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{subst:tfm|help=off|' + (params.templatetype !== 'standard' ? 'type=' + params.templatetype + '|' : '') + '1=' + params.otherTemplateName.replace(new RegExp('^' + Morebits.namespaceRegex([10, 828]) + ':'), '') + '}}'; if (pageobj.getContentModel() === 'sanitized-css') { params.tagText = '/* ' + params.tagText + ' */'; } else { if (params.noinclude) { params.tagText = '<noinclude>' + params.tagText + '</noinclude>'; } params.tagText += params.templatetype === 'standard' || params.templatetype === 'sidebar' || params.templatetype === 'disabled' ? '\n' : ''; // No newline for inline } if (pageobj.canEdit() && ['wikitext', 'sanitized-css'].includes(pageobj.getContentModel())) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Listed for merging with [[:' + params.otherTemplateName + ']]; see [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); if (params.scribunto) { pageobj.setCreateOption('recreate'); // Module /doc might not exist } pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = Twinkle.xfd.callbacks.getDiscussionWikitext(params.xfdcat, params); let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:TfD log}}\n' + added_data; } else { const old_text = pageobj.getPageText(); text = old_text.replace('-->', '-->\n' + added_data); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } } pageobj.setPageText(text); pageobj.setEditSummary('/* ' + Morebits.pageNameNorm + ' */ Adding ' + (params.xfdcat === 'tfd' ? 'deletion nomination' : 'merge listing') + ' of [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, mfd: { main: function(apiobj) { const response = apiobj.getResponse(); const titles = response.query.allpages; // There has been no earlier entries with this prefix, just go on. if (titles.length <= 0) { apiobj.params.numbering = apiobj.params.number = ''; } else { let number = 0; for (let i = 0; i < titles.length; ++i) { const title = titles[i].title; // First, simple test, is there an instance with this exact name? if (title === 'Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm) { number = Math.max(number, 1); continue; } const order_re = new RegExp('^' + Morebits.string.escapeRegExp('Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm) + '\\s*\\(\\s*(\\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\\s*\\)\\s*$'); const match = order_re.exec(title); // No match; A non-good value if (!match) { continue; } // A match, set number to the max of current number = Math.max(number, Number(match[1])); } apiobj.params.number = utils.num2order(parseInt(number, 10) + 1); apiobj.params.numbering = number > 0 ? ' (' + apiobj.params.number + ' nomination)' : ''; } apiobj.params.discussionpage = 'Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm + apiobj.params.numbering; apiobj.statelem.info('next in order is [[' + apiobj.params.discussionpage + ']]'); let wikipedia_page; // Tagging page if (mw.config.get('wgNamespaceNumber') !== 710) { // cannot tag TimedText pages wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging page with deletion tag'); wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.mfd.taggingPage); } // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = apiobj.params.discussionpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Discussion page wikipedia_page = new Morebits.wiki.Page(apiobj.params.discussionpage, 'Creating deletion discussion page'); wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.mfd.discussionPage); // Today's list wikipedia_page = new Morebits.wiki.Page('Wikipedia:Miscellany for deletion', "Adding discussion to today's list"); wikipedia_page.setPageSection(2); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.mfd.todaysList); // Notification to first contributor and/or notification to owner of userspace if (apiobj.params.notifycreator || apiobj.params.notifyuserspace) { const thispage = new Morebits.wiki.Page(mw.config.get('wgPageName')); thispage.setCallbackParameters(apiobj.params); thispage.lookupCreation(Twinkle.xfd.callbacks.mfd.sendNotifications); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(apiobj.params, null); } }, taggingPage: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{' + (params.number === '' ? 'mfd' : 'mfdx|' + params.number) + '|help=off}}'; if (['javascript', 'css', 'sanitized-css'].includes(mw.config.get('wgPageContentModel'))) { params.tagText = '/* ' + params.tagText + ' */\n'; } else { params.tagText += '\n'; if (params.noinclude) { params.tagText = '<noinclude>' + params.tagText + '</noinclude>'; } } if (pageobj.canEdit() && ['wikitext', 'javascript', 'css', 'sanitized-css'].includes(pageobj.getContentModel())) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Nominated for deletion; see [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('nocreate'); pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, discussionPage: function(pageobj) { const params = pageobj.getCallbackParameters(); pageobj.setPageText(Twinkle.xfd.callbacks.getDiscussionWikitext('mfd', params)); pageobj.setEditSummary('Creating deletion discussion page for [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('createonly'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); }, todaysList: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const date = new Morebits.Date(pageobj.getLoadTime()); const date_header = date.format('===MMMM D, YYYY===\n', 'utc'); const date_header_regex = new RegExp(date.format('(===[\\s]*MMMM[\\s]+D,[\\s]+YYYY[\\s]*===)', 'utc')); const added_data = '{{subst:mfd3|pg=' + Morebits.pageNameNorm + params.numbering + '}}'; if (date_header_regex.test(text)) { // we have a section already statelem.info('Found today\'s section, proceeding to add new entry'); text = text.replace(date_header_regex, '$1\n' + added_data); } else { // we need to create a new section statelem.info('No section for today found, proceeding to create one'); text = text.replace('===', date_header + added_data + '\n\n==='); } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchList')); pageobj.setCreateOption('recreate'); pageobj.setDiscussionToolsAutoSubscribe(false); pageobj.save(); }, sendNotifications: function(pageobj) { const initialContrib = pageobj.getCreator(); const params = pageobj.getCallbackParameters(); // Notify the creator if (params.notifycreator) { Twinkle.xfd.callbacks.notifyUser(params, initialContrib); } // Notify the user who owns the subpage if they are not the creator params.userspaceOwner = mw.config.get('wgRelevantUserName'); if (params.notifyuserspace) { if (params.userspaceOwner !== initialContrib) { // Don't log if notifying creator above, will log then Twinkle.xfd.callbacks.notifyUser(params, params.userspaceOwner, params.notifycreator, 'Notifying owner of userspace (' + params.userspaceOwner + ')'); } else if (!params.notifycreator) { // If we thought we would notify the owner but didn't, // then we need to log if we didn't notify the creator // Twinkle.xfd.callbacks.addToLog(params, null); Twinkle.xfd.callbacks.addToLog(params, initialContrib); } } } }, ffd: { taggingImage: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const date = new Morebits.Date(pageobj.getLoadTime()).format('YYYY MMMM D', 'utc'); params.logpage = 'Wikipedia:Files for discussion/' + date; params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; params.tagText = '{{ffd|log=' + date + '|help=off}}\n'; if (pageobj.canEdit()) { text = Twinkle.removeMoveToCommonsTagsFromWikicode(text); pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Listed for discussion at [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('recreate'); // it might be possible for a file to exist without a description page pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Contributor specific edits const wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName')); wikipedia_page.setCallbackParameters(params); wikipedia_page.lookupCreation(Twinkle.xfd.callbacks.ffd.main); }, main: function(pageobj) { // this is coming in from lookupCreation...! const params = pageobj.getCallbackParameters(); const initialContrib = pageobj.getCreator(); params.uploader = initialContrib; // Adding discussion const wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's list"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.ffd.todaysList); // Notification to first contributor if (params.notifycreator) { Twinkle.xfd.callbacks.notifyUser(params, initialContrib); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } }, todaysList: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:FfD log}}'; } pageobj.setPageText(text + '\n\n' + Twinkle.xfd.callbacks.getDiscussionWikitext('ffd', params)); pageobj.setEditSummary('Adding [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, cfd: { main: function(pageobj) { const params = pageobj.getCallbackParameters(); const date = new Morebits.Date(pageobj.getLoadTime()); params.logpage = 'Wikipedia:Categories for discussion/Log/' + date.format('YYYY MMMM D', 'utc'); params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; // Add log/discussion page params to the already-loaded page object pageobj.setCallbackParameters(params); // Tagging category Twinkle.xfd.callbacks.cfd.taggingCategory(pageobj); // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log"; // Adding discussion to list let wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's list"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfd.todaysList); // Notification to first contributor if (params.notifycreator) { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName')); wikipedia_page.setCallbackParameters(params); wikipedia_page.lookupCreation((pageobj) => { Twinkle.xfd.callbacks.notifyUser(pageobj.getCallbackParameters(), pageobj.getCreator()); }); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } }, taggingCategory: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{subst:' + params.xfdcat; let editsummary = (mw.config.get('wgNamespaceNumber') === 14 ? 'Category' : 'Stub template') + ' being considered for ' + params.action; switch (params.xfdcat) { case 'cfd': case 'sfd-t': break; case 'cfc': editsummary += ' to an article'; // falls through case 'cfm': case 'cfr': case 'sfr-t': params.tagText += '|' + params.cfdtarget; break; case 'cfs': params.tagText += '|' + params.cfdtarget + '|' + params.cfdtarget2; break; default: alert('twinklexfd in taggingCategory(): unknown CFD action'); break; } params.tagText += '}}\n'; editsummary += '; see [[:' + params.discussionpage + ']].'; if (pageobj.canEdit()) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary(editsummary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('recreate'); // since categories can be populated without an actual page at that title pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = Twinkle.xfd.callbacks.getDiscussionWikitext(params.xfdcat, params); let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:CfD log}}\n' + added_data; } else { const old_text = pageobj.getPageText(); text = old_text.replace('below this line -->', 'below this line -->\n' + added_data); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } } pageobj.setPageText(text); pageobj.setEditSummary('Adding ' + params.action + ' nomination of [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, cfds: { taggingCategory: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); if (params.xfdcat === 'C2F') { params.tagText = '{{subst:cfm-speedy|1=' + params.cfdstarget.replace(/^:?Category:/, '') + '}}\n'; } else { params.tagText = '{{subst:cfr-speedy|1=' + params.cfdstarget.replace(/^:?Category:/, '') + '}}\n'; } params.discussionpage = ''; // CFDS is just a bullet in a bulleted list. There's no section to link to, so we set this to blank. Blank will be recognized by both the generate userspace log code and the generate userspace log edit summary code as "don't wikilink to a section". if (pageobj.canEdit()) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Listed for speedy renaming; see [[WP:CFDS|Categories for discussion/Speedy]].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('recreate'); // since categories can be populated without an actual page at that title pageobj.save(() => { // No user notification for CfDS, so just add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); }); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); // No user notification for CfDS, so just add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); } }, addToList: function(pageobj) { const old_text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const text = old_text.replace('BELOW THIS LINE -->', 'BELOW THIS LINE -->\n' + Twinkle.xfd.callbacks.getDiscussionWikitext('cfds', params)); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, rfd: { // This gets called both on submit and preview to determine the redirect target findTarget: function(params, callback) { // Used by regular redirects to find the target, but for all redirects, // avoid relying on the client clock to build the log page const query = { action: 'query', curtimestamp: true, format: 'json' }; if (document.getElementById('softredirect')) { // For soft redirects, define the target early // to skip target checks in findTargetCallback params.rfdtarget = document.getElementById('softredirect').textContent.replace(/^:+/, ''); } else { // Find current target of redirect query.titles = mw.config.get('wgPageName'); query.redirects = true; } const wikipedia_api = new Morebits.wiki.Api('Finding target of redirect', query, Twinkle.xfd.callbacks.rfd.findTargetCallback(callback)); wikipedia_api.params = params; wikipedia_api.post(); }, // This is a closure for the callback from the above API request, which gets the target of the redirect findTargetCallback: function(callback) { return function(apiobj) { const response = apiobj.getResponse(); apiobj.params.curtimestamp = response.curtimestamp; if (!apiobj.params.rfdtarget) { // Not a softredirect const target = response.query.redirects && response.query.redirects[0].to; if (!target) { let message = 'No target found. this page does not appear to be a redirect, aborting'; if (mw.config.get('wgAction') === 'history') { message += '. If this is a soft redirect, try again from the content page, not the page history.'; } apiobj.statelem.error(message); return; } apiobj.params.rfdtarget = target; const section = response.query.redirects[0].tofragment; apiobj.params.section = section; } callback(apiobj.params); }; }, main: function(params) { const date = new Morebits.Date(params.curtimestamp); params.logpage = 'Wikipedia:Redirects for discussion/Log/' + date.format('YYYY MMMM D', 'utc'); params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; // Tagging redirect let wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Adding deletion tag to redirect'); wikipedia_page.setFollowRedirect(false); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.rfd.taggingRedirect); // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log"; // Adding discussion wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's log"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.rfd.todaysList); // Notifications if (params.notifycreator || params.relatedpage) { const thispage = new Morebits.wiki.Page(mw.config.get('wgPageName')); thispage.setCallbackParameters(params); thispage.lookupCreation(Twinkle.xfd.callbacks.rfd.sendNotifications); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } }, taggingRedirect: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); // Imperfect for edit request but so be it params.tagText = '{{subst:rfd|' + (mw.config.get('wgNamespaceNumber') === 10 ? 'showontransclusion=1|' : '') + 'content=\n'; if (pageobj.canEdit()) { pageobj.setPageText(params.tagText + text + '\n}}'); pageobj.setEditSummary('Listed for discussion at [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('nocreate'); pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = Twinkle.xfd.callbacks.getDiscussionWikitext('rfd', params); let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:RfD log}}' + added_data; } else { const old_text = pageobj.getPageText(); text = old_text.replace(/(<!-- Add new entries directly below this line\.? -->)/, '$1\n' + added_data); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); }, sendNotifications: function(pageobj) { const initialContrib = pageobj.getCreator(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); // Notifying initial contributor if (params.notifycreator) { Twinkle.xfd.callbacks.notifyUser(params, initialContrib); } // Notifying target page's watchers, if not a soft redirect if (params.relatedpage) { const targetTalk = new mw.Title(params.rfdtarget).getTalkPage(); // On the offchance it's a circular redirect if (params.rfdtarget === mw.config.get('wgPageName')) { statelem.warn('Circular redirect; skipping target page notification'); } else if (document.getElementById('softredirect')) { statelem.warn('Soft redirect; skipping target page notification'); // Don't issue if target talk is the initial contributor's talk or your own } else if (targetTalk.getNamespaceId() === 3 && targetTalk.getMainText() === initialContrib) { statelem.warn('Target is initial contributor; skipping target page notification'); } else if (targetTalk.getNamespaceId() === 3 && targetTalk.getMainText() === mw.config.get('wgUserName')) { statelem.warn('You (' + mw.config.get('wgUserName') + ') are the target; skipping target page notification'); } else { // Don't log if notifying creator above, will log then Twinkle.xfd.callbacks.notifyUser(params, targetTalk.toText(), params.notifycreator, 'Notifying redirect target of the discussion'); return; } // If we thought we would notify the target but didn't, // we need to log if we didn't notify the creator if (!params.notifycreator) { Twinkle.xfd.callbacks.addToLog(params, null); } } } }, rm: { listAtTalk: function(pageobj) { const params = pageobj.getCallbackParameters(); params.discussionpage = pageobj.getPageName(); pageobj.setAppendText('\n\n' + Twinkle.xfd.callbacks.getDiscussionWikitext('rm', params)); pageobj.setEditSummary(`Proposing move of ${ params.currentname .map((currentname, i) => `[[:${currentname}]]${params.newname[i] ? ` to [[:${params.newname[i]}]]` : ''}`) .join(', ') }.`); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('recreate'); // since the talk page need not exist pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.append(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki // add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); }); }, listAtRMTR: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const discussionWikitext = Twinkle.xfd.callbacks.getDiscussionWikitext('rm', params); const newtext = Twinkle.xfd.insertRMTR(text, discussionWikitext); if (text === newtext) { statelem.error('failed to find target spot for the entry'); return; } pageobj.setPageText(newtext); pageobj.setEditSummary(`Adding [[:${params.currentname.join(']], [[:')}]].`); pageobj.setChangeTags(Twinkle.changeTags); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki // add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); }); } } }; /** * Given the wikitext of the WP:RM/TR page and the wikitext to insert, insert it at the bottom of the ==== Uncontroversial technical requests ==== section. * * @param {string} pageWikitext * @param {string} wikitextToInsert Will typically be `{{subst:RMassist|1=From|2=To|reason=Reason}}`, which expands out to `* {{RMassist/core | 1 = From | 2 = To | discuss = yes | reason = Reason | sig = Signature | requester = YourUserName}}` * @return {string} pageWikitext */ Twinkle.xfd.insertRMTR = function(pageWikitext, wikitextToInsert) { // [^\n]* is for matching anchors, e.g. <span class="anchor" id="*"></span> const placementRE = /\n{1,}(====[^\n]*Requests to revert undiscussed moves ?====)/i; return pageWikitext.replace(placementRE, '\n' + wikitextToInsert + '\n\n$1'); }; Twinkle.xfd.callback.evaluate = function(e) { const form = e.target; const params = Morebits.QuickForm.getInputData(form); Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Twinkle.xfd.currentRationale = params.reason; Morebits.Status.onError(Twinkle.xfd.printRationale); let query, wikipedia_page, wikipedia_api; switch (params.venue) { case 'afd': // AFD query = { action: 'query', list: 'allpages', apprefix: 'Articles for deletion/' + Morebits.pageNameNorm, apnamespace: 4, apfilterredir: 'nonredirects', aplimit: 'max', // 500 is max for normal users, 5000 for bots and sysops format: 'json' }; wikipedia_api = new Morebits.wiki.Api('Tagging article with deletion tag', query, Twinkle.xfd.callbacks.afd.main); wikipedia_api.params = params; wikipedia_api.post(); break; case 'tfd': // TFD if (params.tfdtarget) { // remove namespace name params.tfdtarget = utils.stripNs(params.tfdtarget); } // Modules can't be tagged, TfD instructions are to place on /doc subpage params.scribunto = mw.config.get('wgPageContentModel') === 'Scribunto'; if (params.xfdcat === 'tfm') { // Merge // Tag this template/module if (params.scribunto) { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName') + '/doc', 'Tagging this module documentation with merge tag'); params.otherTemplateName = 'Module:' + params.tfdtarget; } else { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging this template with merge tag'); params.otherTemplateName = 'Template:' + params.tfdtarget; } } else { // delete if (params.scribunto) { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName') + '/doc', 'Tagging module documentation with deletion tag'); } else { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging template with deletion tag'); } } wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.tfd.main); break; case 'mfd': // MFD query = { action: 'query', list: 'allpages', apprefix: 'Miscellany for deletion/' + Morebits.pageNameNorm, apnamespace: 4, apfilterredir: 'nonredirects', aplimit: 'max', // 500 is max for normal users, 5000 for bots and sysops format: 'json' }; wikipedia_api = new Morebits.wiki.Api('Looking for prior nominations of this page', query, Twinkle.xfd.callbacks.mfd.main); wikipedia_api.params = params; wikipedia_api.post(); break; case 'ffd': // FFD // Tagging file // A little out of order with this coming before 'main', // but tagging doesn't need the uploader parameter, // while everything else does, so tag then get the uploader wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Adding deletion tag to file page'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.ffd.taggingImage); break; case 'cfd': if (params.cfdtarget) { params.cfdtarget = utils.stripNs(params.cfdtarget); } else { params.cfdtarget = ''; // delete } if (params.cfdtarget2) { // split params.cfdtarget2 = utils.stripNs(params.cfdtarget2); } // Used for customized actions in edit summaries and the notification template var summaryActions = { cfd: 'deletion', 'sfd-t': 'deletion', cfm: 'merging', cfr: 'renaming', 'sfr-t': 'renaming', cfs: 'splitting', cfc: 'conversion' }; params.action = summaryActions[params.xfdcat]; // Tagging category wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging category with ' + params.action + ' tag'); wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfd.main); break; case 'cfds': // add namespace name if missing params.cfdstarget = utils.addNs(params.cfdstarget, 14); var logpage = 'Wikipedia:Categories for discussion/Speedy'; // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = logpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Tagging category wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging category with rename tag'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfds.taggingCategory); // Adding discussion to list wikipedia_page = new Morebits.wiki.Page(logpage, 'Adding discussion to the list'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfds.addToList); break; case 'rfd': // find target and pass main as the callback Twinkle.xfd.callbacks.rfd.findTarget(params, Twinkle.xfd.callbacks.rfd.main); break; case 'rm': var nomPageName = params.rmtr ? 'Wikipedia:Requested moves/Technical requests' : new mw.Title(Morebits.pageNameNorm).getTalkPage().toText(); Morebits.wiki.actionCompleted.redirect = nomPageName; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; wikipedia_page = new Morebits.wiki.Page(nomPageName, params.rmtr ? 'Adding entry at WP:RM/TR' : 'Adding entry on talk page'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); if (params.rmtr) { wikipedia_page.load(Twinkle.xfd.callbacks.rm.listAtRMTR); } else { // listAtTalk uses .append(), so no need to load the page Twinkle.xfd.callbacks.rm.listAtTalk(wikipedia_page); } break; default: alert('twinklexfd: unknown XFD discussion venue'); break; } }; Twinkle.addInitCallback(Twinkle.xfd, 'xfd'); }()); // </nowiki> nbftc26fftb464h06xeih9vufaapuhu 365978 365977 2026-08-20T12:12:07Z ChiK 1136 ೧ revisions imported from [[:en:MediaWiki:Gadget-twinklexfd.js]] 365977 javascript text/javascript // <nowiki> (function() { /* **************************************** *** twinklexfd.js: XFD module **************************************** * Mode of invocation: Tab ("XFD") * Active on: Existing, non-special pages, except for file pages with no local (non-Commons) file which are not redirects */ Twinkle.xfd = function twinklexfd() { // Disable on: // * special pages // * non-existent pages // * files on Commons, whether there is a local page or not (unneeded local pages of files on Commons are eligible for CSD F2, or R4 if it's a redirect) if (mw.config.get('wgNamespaceNumber') < 0 || !mw.config.get('wgArticleId') || (mw.config.get('wgNamespaceNumber') === 6 && document.getElementById('mw-sharedupload'))) { return; } let tooltip = 'Start a discussion for deleting'; if (mw.config.get('wgIsRedirect')) { tooltip += ' or retargeting this redirect'; } else { switch (mw.config.get('wgNamespaceNumber')) { case 0: tooltip += ' or moving this article'; break; case 10: tooltip += ' or merging this template'; break; case 828: tooltip += ' or merging this module'; break; case 6: tooltip += ' this file'; break; case 14: tooltip += ', merging or renaming this category'; break; default: tooltip += ' this page'; break; } } Twinkle.addPortletLink(Twinkle.xfd.callback, 'XFD', 'tw-xfd', tooltip); }; const utils = { /** Get ordinal number figure */ num2order: function(num) { switch (num) { case 1: return ''; case 2: return '2nd'; case 3: return '3rd'; default: return num + 'th'; } }, /** * Remove namespace name from title if present * Exception-safe wrapper around mw.Title * * @param {string} title */ stripNs: function(title) { const title_obj = mw.Title.newFromUserInput(title); if (!title_obj) { return title; // user entered invalid input; do nothing } return title_obj.getMainText(); }, /** * Add namespace name to page title if not already given * CAUTION: namespace name won't be added if a namespace (*not* necessarily * the same as the one given) already is there in the title * * @param {string} title * @param {number} namespaceNumber */ addNs: function(title, namespaceNumber) { const title_obj = mw.Title.newFromUserInput(title, namespaceNumber); if (!title_obj) { return title; // user entered invalid input; do nothing } return title_obj.toText(); }, /** * Provide Wikipedian TLA style: AfD, RfD, CfDS, RM, SfD, etc. * * @param {string} venue * @return {string} */ toTLACase: function(venue) { return venue .toString() // Everybody up, inclduing rm and the terminal s in cfds .toUpperCase() // Lowercase the central f in a given TLA and normalize sfd-t and sfr-t .replace(/(.)F(.)(?:-.)?/, '$1f$2'); } }; Twinkle.xfd.currentRationale = null; // error callback on Morebits.Status.object Twinkle.xfd.printRationale = function twinklexfdPrintRationale() { if (Twinkle.xfd.currentRationale) { Morebits.Status.printUserText(Twinkle.xfd.currentRationale, 'Your deletion rationale is provided below, which you can copy and paste into a new XFD dialog if you wish to try again:'); // only need to print the rationale once Twinkle.xfd.currentRationale = null; } }; Twinkle.xfd.callback = function twinklexfdCallback() { const Window = new Morebits.SimpleWindow(700, 400); Window.setTitle('Start a deletion discussion (XfD)'); Window.setScriptName('Twinkle'); Window.addFooterLink('About deletion discussions', 'WP:XFD'); Window.addFooterLink('XfD prefs', 'WP:TW/PREF#xfd'); Window.addFooterLink('Twinkle help', 'WP:TW/DOC#xfd'); Window.addFooterLink('Give feedback', 'WT:TW'); const form = new Morebits.QuickForm(Twinkle.xfd.callback.evaluate); const categories = form.append({ type: 'select', name: 'venue', label: 'Deletion discussion venue:', tooltip: 'When activated, a default choice is made, based on what namespace you are in. This default should be the most appropriate.', event: Twinkle.xfd.callback.change_category }); const namespace = mw.config.get('wgNamespaceNumber'); categories.append({ type: 'option', label: 'AfD (Articles for deletion)', selected: namespace === 0, // Main namespace value: 'afd' }); categories.append({ type: 'option', label: 'TfD (Templates for discussion)', selected: [10, 828].includes(namespace), // Template and module namespaces value: 'tfd' }); categories.append({ type: 'option', label: 'FfD (Files for discussion)', selected: namespace === 6, // File namespace value: 'ffd' }); categories.append({ type: 'option', label: 'CfD (Categories for discussion)', selected: namespace === 14 || (namespace === 10 && /-stub$/.test(Morebits.pageNameNorm)), // Category namespace and stub templates value: 'cfd' }); categories.append({ type: 'option', label: 'CfD/S (Categories for speedy renaming)', value: 'cfds' }); categories.append({ type: 'option', label: 'MfD (Miscellany for deletion)', selected: ![0, 6, 10, 14, 828].includes(namespace) || Morebits.pageNameNorm.indexOf('Template:User ', 0) === 0, // Other namespaces, and userboxes in template namespace value: 'mfd' }); categories.append({ type: 'option', label: 'RfD (Redirects for discussion)', selected: mw.config.get('wgIsRedirect'), value: 'rfd' }); categories.append({ type: 'option', label: 'RM (Requested moves)', selected: false, value: 'rm' }); form.append({ type: 'div', id: 'wrong-venue-warn', style: 'color: red; font-style: italic' }); form.append({ type: 'checkbox', list: [ { label: 'Notify page creator if possible', value: 'notify', name: 'notifycreator', tooltip: "A notification template will be placed on the creator's talk page if this is true.", checked: true } ] }); form.append({ type: 'field', label: 'Work area', name: 'work_area' }); const previewlink = document.createElement('a'); $(previewlink).on('click', () => { Twinkle.xfd.callbacks.preview(result); // |result| is defined below }); previewlink.style.cursor = 'pointer'; previewlink.textContent = 'Preview'; form.append({ type: 'div', id: 'xfdpreview', label: [previewlink] }); form.append({ type: 'div', id: 'twinklexfd-previewbox', style: 'display: none' }); form.append({ type: 'submit' }); var result = form.render(); Window.setContent(result); Window.display(); result.previewer = new Morebits.wiki.Preview($(result).find('div#twinklexfd-previewbox').last()[0]); // We must init the controls const evt = document.createEvent('Event'); evt.initEvent('change', true, true); result.venue.dispatchEvent(evt); }; Twinkle.xfd.callback.wrongVenueWarning = function twinklexfdWrongVenueWarning(venue) { let text = ''; const namespace = mw.config.get('wgNamespaceNumber'); switch (venue) { case 'afd': if (namespace !== 0) { text = 'AfD is generally appropriate only for articles.'; } else if (mw.config.get('wgIsRedirect')) { text = 'Please use RfD for redirects.'; } break; case 'tfd': if (namespace === 10 && /-stub$/.test(Morebits.pageNameNorm)) { text = 'Use CfD for stub templates.'; } else if (Morebits.pageNameNorm.indexOf('Template:User ') === 0) { text = 'Please use MfD for userboxes'; } break; case 'cfd': if (![10, 14].includes(namespace)) { text = 'CfD is only for categories and stub templates.'; } break; case 'cfds': if (namespace !== 14) { text = 'CfDS is only for categories.'; } break; case 'ffd': if (namespace !== 6) { text = 'FFD is selected but this page doesn\'t look like a file!'; } break; case 'rm': if (namespace === 14) { // category text = 'Please use CfD or CfDS for category renames.'; } else if ([118, 119, 2, 3].includes(namespace)) { // draft, draft talk, user, user talk text = 'RMs are not permitted in draft and userspace, unless they are uncontroversial technical requests.'; } break; default: // mfd or rfd break; } $('#wrong-venue-warn').text(text); }; Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory(e) { const value = e.target.value; const form = e.target.form; const old_area = Morebits.QuickForm.getElements(e.target.form, 'work_area')[0]; let work_area = null; const oldreasontextbox = form.getElementsByTagName('textarea')[0]; const oldreason = oldreasontextbox ? oldreasontextbox.value : ''; const appendReasonBox = function twinklexfdAppendReasonBox() { work_area.append({ type: 'textarea', name: 'reason', label: 'Reason:', value: oldreason, tooltip: 'You can use wikimarkup in your reason. Twinkle will automatically sign your post.' }); }; Twinkle.xfd.callback.wrongVenueWarning(value); form.previewer.closePreview(); switch (value) { case 'afd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Articles for deletion', name: 'work_area' }); work_area.append({ type: 'div', label: '', // Added later by Twinkle.makeFindSourcesDiv() id: 'twinkle-xfd-findsources', style: 'margin-bottom: 5px; margin-top: -5px;' }); work_area.append({ type: 'select', name: 'outcome', label: 'Desired outcome:', event: Twinkle.xfd.callbacks.changeAfdOutcome, list: [ { label: 'Delete', value: 'deletion' }, { label: 'Merge', value: 'merging' }, { label: 'Redirect', value: 'redirecting' }, { label: 'Draftify', value: 'draftification' } ] }); work_area.append({ name: 'afdtarget', type: 'input', label: 'Target page:', tooltip: 'Target page for the redirect or merge.', event: Twinkle.xfd.callbacks.changeAfdTarget }); work_area.append({ type: 'checkbox', list: [ { label: 'Wrap deletion tag with &lt;noinclude&gt;', value: 'noinclude', name: 'noinclude', tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t transclude. This option is not normally required.' } ] }); work_area.append({ type: 'select', name: 'xfdcat', label: 'Choose what category this nomination belongs in:', list: [ { label: 'Unknown', value: '?', selected: true }, { label: 'Media and music', value: 'M' }, { label: 'Organisation, corporation, or product', value: 'O' }, { label: 'Biographical', value: 'B' }, { label: 'Society topics', value: 'S' }, { label: 'Web or internet', value: 'W' }, { label: 'Games or sports', value: 'G' }, { label: 'Science and technology', value: 'T' }, { label: 'Fiction and the arts', value: 'F' }, { label: 'Places and transportation', value: 'P' }, { label: 'Indiscernible or unclassifiable topic', value: 'I' }, { label: 'Debate not yet sorted', value: 'U' } ] }); work_area.append({ type: 'select', multiple: true, name: 'delsortCats', label: 'Choose deletion sorting categories:', tooltip: 'Select a few categories that are specifically relevant to the subject of the article. Be as precise as possible; categories like People and USA should only be used when no other categories apply.' }); // grab deletion sort categories from en-wiki Morebits.wiki.getCachedJson('Wikipedia:WikiProject_Deletion_sorting/Computer-readable.json').then((delsortCategories) => { const $select = $('[name="delsortCats"]'); $.each(delsortCategories, (groupname, list) => { const $optgroup = $('<optgroup>').attr('label', groupname); const $delsortCat = $select.append($optgroup); list.forEach((item) => { const $option = $('<option>').val(item).text(item); $delsortCat.append($option); }); }); }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); // Now that we've rendered the form, hide the target text box. Unhide it later for certain outcomes, using a callback. $('[name="afdtarget"]').parent().hide(); Twinkle.makeFindSourcesDiv('#twinkle-xfd-findsources'); $(work_area).find('[name=delsortCats]') .attr('data-placeholder', 'Select delsort pages') .select2({ theme: 'default select2-morebits', width: '100%', matcher: Morebits.select2.matcher, templateResult: Morebits.select2.highlightSearchMatches, language: { searching: Morebits.select2.queryInterceptor }, // Link text to the page itself templateSelection: function(choice) { return $('<a>').text(choice.text).attr({ href: mw.util.getUrl('Wikipedia:WikiProject_Deletion_sorting/' + choice.text), target: '_blank' }); } }); mw.util.addCSS( // Remove black border '.select2-container--default.select2-container--focus .select2-selection--multiple { border: 1px solid #aaa; }' + // Reduce padding '.select2-results .select2-results__option { padding-top: 1px; padding-bottom: 1px; }' + '.select2-results .select2-results__group { padding-top: 1px; padding-bottom: 1px; } ' + // Adjust font size '.select2-container .select2-dropdown .select2-results { font-size: 13px; }' + '.select2-container .selection .select2-selection__rendered { font-size: 13px; }' + // Make the tiny cross larger '.select2-selection__choice__remove { font-size: 130%; }' ); break; case 'tfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Templates for discussion', name: 'work_area' }); var templateOrModule = mw.config.get('wgPageContentModel') === 'Scribunto' ? 'module' : 'template'; work_area.append({ type: 'select', label: 'Choose type of action wanted:', name: 'xfdcat', event: function(e) { const target = e.target; let tfdtarget = target.form.tfdtarget; // add/remove extra input box if (target.value === 'tfm' && !tfdtarget) { tfdtarget = new Morebits.QuickForm.Element({ name: 'tfdtarget', type: 'input', label: 'Other ' + templateOrModule + ' to be merged:', tooltip: 'Required. Should not include the ' + Morebits.string.toUpperCaseFirstChar(templateOrModule) + ': namespace prefix.', required: true }); target.parentNode.appendChild(tfdtarget.render()); } else { $(Morebits.QuickForm.getElementContainer(tfdtarget)).remove(); tfdtarget = null; } }, list: [ { label: 'Deletion', value: 'tfd', selected: true }, { label: 'Merge', value: 'tfm' } ] }); work_area.append({ type: 'select', name: 'templatetype', label: 'Deletion tag display style:', tooltip: 'Which <code>type=</code> parameter to pass to the TfD tag template.', list: templateOrModule === 'module' ? [ { value: 'module', label: 'Module', selected: true } ] : [ { value: 'standard', label: 'Standard', selected: true }, { value: 'sidebar', label: 'Sidebar/infobox', selected: $('.infobox').length }, { value: 'inline', label: 'Inline template', selected: (() => { // TODO: clean up after legacy parser is permanently replaced with Parsoid const $oldParser = $('.mw-parser-output > p .Inline-Template'); const $newParser = $('.mw-parser-output section[data-mw-section-id="0"] p .Inline-Template'); const $scanTarget = $newParser.length ? $newParser : $oldParser; return $scanTarget.length; })() }, { value: 'tiny', label: 'Tiny inline' }, { value: 'disabled', label: 'Disabled' } ] }); work_area.append({ type: 'checkbox', list: [ { label: 'Wrap deletion tag with &lt;noinclude&gt; (for substituted templates only)', value: 'noinclude', name: 'noinclude', tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t get substituted along with the template.', disabled: templateOrModule === 'module', checked: !!$('.box-Subst_only').length // Default to checked if page carries {{subst only}} } ] }); work_area.append({ type: 'checkbox', list: [ { label: 'Notify talk pages of affected user scripts', value: 'devpages', name: 'devpages', tooltip: 'A notification will be sent to Twinkle, AWB, and Ultraviolet\'s talk pages if those user scripts are marked as using this template.', checked: true } ] }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'mfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Miscellany for deletion', name: 'work_area' }); if (mw.config.get('wgNamespaceNumber') !== 710) { // TimedText cannot be tagged, so asking whether to noinclude the tag is pointless work_area.append({ type: 'checkbox', list: [ { label: 'Wrap deletion tag with &lt;noinclude&gt;', value: 'noinclude', name: 'noinclude', tooltip: 'Will wrap the deletion tag in &lt;noinclude&gt; tags, so that it won\'t transclude. Select this option for userboxes.' } ] }); } if ((mw.config.get('wgNamespaceNumber') === 2 /* User: */ || mw.config.get('wgNamespaceNumber') === 3 /* User talk: */) && mw.config.exists('wgRelevantUserName')) { work_area.append({ type: 'checkbox', list: [ { label: 'Notify owner of userspace (if they are not the page creator)', value: 'notifyuserspace', name: 'notifyuserspace', tooltip: 'If the user in whose userspace this page is located is not the page creator (for example, the page is a rescued article stored as a userspace draft), notify the userspace owner as well.', checked: true } ] }); } appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'ffd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Discussion venues for files', name: 'work_area' }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'cfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Categories for discussion', name: 'work_area' }); var isCategory = mw.config.get('wgNamespaceNumber') === 14; work_area.append({ type: 'select', label: 'Choose type of action wanted:', name: 'xfdcat', event: function(e) { const value = e.target.value, cfdtarget = e.target.form.cfdtarget; let cfdtarget2 = e.target.form.cfdtarget2; // update enabled status cfdtarget.disabled = value === 'cfd' || value === 'sfd-t'; if (isCategory) { // update label if (value === 'cfs') { Morebits.QuickForm.setElementLabel(cfdtarget, 'Target categories: '); } else if (value === 'cfc') { Morebits.QuickForm.setElementLabel(cfdtarget, 'Target article: '); } else { Morebits.QuickForm.setElementLabel(cfdtarget, 'Target category: '); } // add/remove extra input box if (value === 'cfs') { if (cfdtarget2) { cfdtarget2.disabled = false; $(cfdtarget2).show(); } else { cfdtarget2 = document.createElement('input'); cfdtarget2.setAttribute('name', 'cfdtarget2'); cfdtarget2.setAttribute('type', 'text'); cfdtarget2.setAttribute('required', 'true'); cfdtarget.parentNode.appendChild(cfdtarget2); } } else { $(cfdtarget2).prop('disabled', true); $(cfdtarget2).hide(); } } else { // Update stub template label Morebits.QuickForm.setElementLabel(cfdtarget, 'Target stub template: '); } }, list: isCategory ? [ { label: 'Deletion', value: 'cfd', selected: true }, { label: 'Merge', value: 'cfm' }, { label: 'Renaming', value: 'cfr' }, { label: 'Split', value: 'cfs' }, { label: 'Convert into article', value: 'cfc' } ] : [ { label: 'Stub Deletion', value: 'sfd-t', selected: true }, { label: 'Stub Renaming', value: 'sfr-t' } ] }); work_area.append({ type: 'input', name: 'cfdtarget', label: 'Target category:', // default, changed above disabled: true, required: true, // only when enabled value: '' }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'cfds': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Categories for speedy renaming', name: 'work_area' }); work_area.append({ type: 'select', label: 'C2 sub-criterion:', name: 'xfdcat', tooltip: 'See WP:CFDS for full explanations.', list: [ { label: 'C2A: Typographic and spelling fixes', value: 'C2A', selected: true }, { label: 'C2B: Naming conventions and disambiguation', value: 'C2B' }, { label: 'C2C: Consistency with names of similar categories', value: 'C2C' }, { label: 'C2D: Rename to match article name', value: 'C2D' }, { label: 'C2E: Author request', value: 'C2E' }, { label: 'C2F: One eponymous article', value: 'C2F' } ] }); work_area.append({ type: 'input', name: 'cfdstarget', label: 'New name:', size: 70, value: '', required: true }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'rfd': work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Redirects for discussion', name: 'work_area' }); work_area.append({ type: 'checkbox', list: [ { label: 'Notify target page if possible', value: 'relatedpage', name: 'relatedpage', tooltip: "A notification template will be placed on the talk page of this redirect's target if this is true.", checked: true } ] }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; case 'rm': { work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Requested moves', name: 'work_area' }); work_area.append({ type: 'checkbox', list: [ { label: 'Uncontroversial technical request', value: 'rmtr', name: 'rmtr', tooltip: 'Use this option when you are unable to perform this uncontroversial move yourself because of a technical reason (e.g. a page already exists at the new title, or the page is protected)', checked: false, event: function() { $('input[name="newname"]', form).prop('required', this.checked); $('input[type="button"][value="more"]', form)[0].sublist.inputs[1].required = this.checked; }, subgroup: { type: 'checkbox', list: [ { label: 'Opt out of discussion if the request is contested', value: 'rmtr-discuss', name: 'rmtr-discuss', tooltip: 'Use this option if you prefer to withdraw the request if contested, rather than discuss it. This suppresses the "discuss" link, which may be used to convert your request to a discussion on the talk page.', checked: false } ] } } ] }); work_area.append({ type: 'dyninput', inputs: [ { label: 'From:', name: 'currentname', required: true }, { label: 'To:', name: 'newname', tooltip: 'Required for technical requests. Otherwise, if unsure of the appropriate title, you may leave it blank.' } ], min: 1 }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); const currentNonTalkPage = mw.Title.newFromText(Morebits.pageNameNorm).getSubjectPage().toText(); form.currentname.value = currentNonTalkPage; break; } default: work_area = new Morebits.QuickForm.Element({ type: 'field', label: 'Nothing for anything', name: 'work_area' }); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); break; } // Return to checked state when switching, but no creator notification for CFDS or RM form.notifycreator.disabled = value === 'cfds' || value === 'rm'; form.notifycreator.checked = !form.notifycreator.disabled; }; Twinkle.xfd.callbacks = { /** If the user hasn't modified the reason much, modify the reason to include the target article. If the user has modified the reason box with a custom reason, do nothing, since we don't want to blank their work. */ changeAfdTarget: function() { const $afdTarget = $('[name="afdtarget"]'); const $reason = $('[name="reason"]'); const $outcome = $('[name="outcome"]'); if ($reason.val().endsWith('because ')) { // Target has something typed in if ($afdTarget.val()) { if ($outcome.val() === 'redirecting') { $reason.val(`I propose '''redirecting''' to [[${$afdTarget.val()}]] because `); } else if ($outcome.val() === 'merging') { $reason.val(`I propose '''merging''' to [[${$afdTarget.val()}]] because `); } // Target is blank } else { if ($outcome.val() === 'redirecting') { $reason.val("I propose '''redirecting''' because "); } else if ($outcome.val() === 'merging') { $reason.val("I propose '''merging''' because "); } } } }, /** Print a default reason in the reason textarea, depending on which outcome is selected from the outcome dropdown list. */ changeAfdOutcome: function() { const $outcome = $('[name="outcome"]'); const $reason = $('[name="reason"]'); const $afdTarget = $('[name="afdtarget"]'); $afdTarget.val(''); if ($outcome.val() === 'redirecting') { $reason.val("I propose '''redirecting''' because "); $afdTarget.parent().show(); } else if ($outcome.val() === 'merging') { $reason.val("I propose '''merging''' because "); $afdTarget.parent().show(); } else if ($outcome.val() === 'draftification') { $reason.val("I propose '''draftifying''' because "); $afdTarget.parent().hide(); } else if ($outcome.val() === 'deletion') { $reason.val(''); $afdTarget.parent().hide(); } }, /** Requires having the tag text (params.tagText) set ahead of time */ autoEditRequest: function(pageobj, params) { const talkName = new mw.Title(pageobj.getPageName()).getTalkPage().toText(); if (talkName === pageobj.getPageName()) { pageobj.getStatusElement().error('Page protected and nowhere to add an edit request, aborting'); } else { pageobj.getStatusElement().warn('Page protected, requesting edit'); const editRequest = '{{subst:Xfd edit protected|page=' + pageobj.getPageName() + '|discussion=' + params.discussionpage + (params.venue === 'rfd' ? '|rfd=yes' : '') + '|tag=<nowiki>' + params.tagText + '\u003C/nowiki>}}'; // U+003C: < const talk_page = new Morebits.wiki.Page(talkName, 'Automatically posting edit request on talk page'); talk_page.setNewSectionTitle('Edit request to complete ' + utils.toTLACase(params.venue) + ' nomination'); talk_page.setNewSectionText(editRequest); talk_page.setCreateOption('recreate'); talk_page.setWatchlist(Twinkle.getPref('xfdWatchPage')); talk_page.setFollowRedirect(true); // should never be needed, but if the article is moved, we would want to follow the redirect talk_page.setChangeTags(Twinkle.changeTags); talk_page.setCallbackParameters(params); talk_page.newSection(null, () => { talk_page.getStatusElement().warn('Unable to add edit request, the talk page may be protected'); }); } }, getDiscussionWikitext: function(venue, params) { if (venue === 'cfds') { // CfD/S takes a completely different style return '* [[:' + Morebits.pageNameNorm + ']] to [[:' + params.cfdstarget + ']]\u00A0\u2013 ' + params.xfdcat + (params.reason ? ': ' + Morebits.string.formatReasonText(params.reason) : '.') + ' ~~~~'; // U+00A0 NO-BREAK SPACE; U+2013 EN RULE } if (venue === 'rm') { if (params.rmtr) { const rmtrDiscuss = params['rmtr-discuss'] ? '|discuss=no' : ''; return params.currentname .map((currentname, i) => `{{subst:RMassist|1=${currentname}|2=${params.newname[i]}${rmtrDiscuss}|reason=${params.reason}}}`) .join('\n'); } return `{{subst:Requested move${ params.currentname .map((currentname, i) => `|current${i + 1}=${currentname}|new${i + 1}=${params.newname[i]}`) .join('') }|reason=${params.reason}}}`; } let text = '{{subst:' + venue + '2'; const reasonKey = venue === 'ffd' ? 'Reason' : 'text'; // Add a reason unconditionally, so that at least a signature is added text += '|' + reasonKey + '=' + Morebits.string.formatReasonText(params.reason, true); if (venue === 'afd' || venue === 'mfd') { text += '|pg=' + Morebits.pageNameNorm; if (venue === 'afd') { text += '|cat=' + params.xfdcat; } } else if (venue === 'rfd') { text += '|redirect=' + Morebits.pageNameNorm; } else { text += '|1=' + mw.config.get('wgTitle'); if (mw.config.get('wgPageContentModel') === 'Scribunto') { text += '|module=Module:'; } } if (params.rfdtarget) { text += '|target=' + params.rfdtarget + (params.section ? '#' + params.section : ''); } else if (params.tfdtarget) { text += '|2=' + params.tfdtarget; } else if (params.cfdtarget) { text += '|2=' + params.cfdtarget; if (params.cfdtarget2) { text += '|3=' + params.cfdtarget2; } } else if (params.uploader) { text += '|Uploader=' + params.uploader; } text += '}}'; if (venue === 'rfd' || venue === 'tfd' || venue === 'cfd') { text += '\n'; } // Don't delsort if delsortCats is undefined (TFD, FFD, etc.) // Don't delsort if delsortCats is an empty array (AFD where user chose no categories) if (Array.isArray(params.delsortCats) && params.delsortCats.length) { text += '\n{{subst:Deletion sorting/multi|' + params.delsortCats.join('|') + '|sig=~~~~}}'; } return text; }, showPreview: function(form, venue, params) { const templatetext = Twinkle.xfd.callbacks.getDiscussionWikitext(venue, params); if (venue === 'rm') { // RM templates are sensitive to page title form.previewer.beginRender(templatetext, params.rmtr ? 'Wikipedia:Requested moves/Technical requests' : new mw.Title(Morebits.pageNameNorm).getTalkPage().toText()); } else { form.previewer.beginRender(templatetext, 'WP:TW'); // Force wikitext } }, preview: function(form) { // venue, reason, xfdcat, tfdtarget, cfdtarget, cfdtarget2, cfdstarget, delsortCats, newname const params = Morebits.QuickForm.getInputData(form); const venue = params.venue; // Remove CfD or TfD namespace prefixes if given if (params.tfdtarget) { params.tfdtarget = utils.stripNs(params.tfdtarget); } else if (params.cfdtarget) { params.cfdtarget = utils.stripNs(params.cfdtarget); if (params.cfdtarget2) { params.cfdtarget2 = utils.stripNs(params.cfdtarget2); } } else if (params.cfdstarget) { // Add namespace if not given (CFDS) params.cfdstarget = utils.addNs(params.cfdstarget, 14); } if (venue === 'ffd') { // Fetch the uploader const page = new Morebits.wiki.Page(mw.config.get('wgPageName')); page.lookupCreation(() => { params.uploader = page.getCreator(); Twinkle.xfd.callbacks.showPreview(form, venue, params); }); } else if (venue === 'rfd') { // Find the target Twinkle.xfd.callbacks.rfd.findTarget(params, (params) => { Twinkle.xfd.callbacks.showPreview(form, venue, params); }); } else if (venue === 'cfd') { // Swap in CfD subactions Twinkle.xfd.callbacks.showPreview(form, params.xfdcat, params); } else { Twinkle.xfd.callbacks.showPreview(form, venue, params); } }, /** * Unified handler for sending {{Xfd notice}} notifications * Also handles userspace logging * * @param {Object} params * @param {string} notifyTarget The user or page being notified * @param {boolean} [noLog=false] Whether to skip logging to userspace * XfD log, especially useful in cases in where multiple notifications * may be sent out (MfD, TfM, RfD) * @param {string} [actionName] Alternative description of the action * being undertaken. Required if not notifying a user talk page. */ notifyUser: function(params, notifyTarget, noLog, actionName) { // Ensure items with User talk or no namespace prefix both end // up at user talkspace as expected, but retain the // prefix-less username for addToLog const userTalkNamespace = 3; notifyTarget = mw.Title.newFromText(notifyTarget, userTalkNamespace); const targetNS = notifyTarget.getNamespaceId(); const usernameOrTarget = notifyTarget.getRelativeText(userTalkNamespace); notifyTarget = notifyTarget.toText(); if (targetNS === userTalkNamespace) { // Disallow warning yourself if (usernameOrTarget === mw.config.get('wgUserName')) { Morebits.Status.warn('You (' + usernameOrTarget + ') created this page; skipping user notification'); // if we thought we would notify someone but didn't, // then jump to logging. Twinkle.xfd.callbacks.addToLog(params, null); return; } // Default is notifying the initial contributor, but MfD also // notifies userspace page owner actionName = actionName || 'Notifying initial contributor (' + usernameOrTarget + ')'; } const notifyText = Twinkle.xfd.callbacks.generateUserTalkNoticeWikitext(params); // Link to the venue; object used here rather than repetitive items in switch const venueNames = { afd: 'Articles for deletion', tfd: 'Templates for discussion', mfd: 'Miscellany for deletion', cfd: 'Categories for discussion', ffd: 'Files for discussion', rfd: 'Redirects for discussion' }; const editSummary = 'Notification: [[' + params.discussionpage + '|listing]] of [[:' + Morebits.pageNameNorm + ']] at [[WP:' + venueNames[params.venue] + ']].'; const usertalkpage = new Morebits.wiki.Page(notifyTarget, actionName); usertalkpage.setAppendText(notifyText); usertalkpage.setEditSummary(editSummary); usertalkpage.setChangeTags(Twinkle.changeTags); usertalkpage.setCreateOption('recreate'); // Different pref for RfD target notifications if (params.venue === 'rfd' && targetNS !== 3) { usertalkpage.setWatchlist(Twinkle.getPref('xfdWatchRelated')); } else { usertalkpage.setWatchlist(Twinkle.getPref('xfdWatchUser')); } usertalkpage.setFollowRedirect(true, false); if (noLog) { usertalkpage.append(); } else { usertalkpage.append(() => { // Don't treat RfD target or MfD userspace owner as initialContrib in log if (!params.notifycreator) { notifyTarget = null; } // add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, usernameOrTarget); }, () => { // if user could not be notified, log nomination without mentioning that notification was sent Twinkle.xfd.callbacks.addToLog(params, null); }); } }, generateUserTalkNoticeWikitext: function(params) { // For grep: Afd notice, Mfd notice, Tfd notice, Cfd notice, Ffd notice, Rfd notice let notifytext = '\n{{subst:' + params.venue + ' notice'; const templateNamespace = 10; // Venue-specific parameters switch (params.venue) { case 'afd': notifytext += params.outcome !== 'deletion' ? '|outcome=' + params.outcome : ''; notifytext += params.afdtarget ? '|target=' + params.afdtarget : ''; // Tell the template to add " (Xnd nomination)" to the XFD title, if needed. // The &#32; (HTML space character) is needed to overcome MediaWiki's parameter auto-trim. notifytext += params.numbering !== '' ? '|order=&#32;' + params.numbering : ''; break; case 'mfd': // tell the template to add " (Xnd nomination)" to the XFD title, if needed notifytext += params.numbering !== '' ? '|order=&#32;' + params.numbering : ''; break; case 'tfd': if (params.xfdcat === 'tfm') { notifytext = '\n{{subst:Tfm notice|2=' + params.tfdtarget; } break; case 'cfd': notifytext += '|action=' + params.action + (mw.config.get('wgNamespaceNumber') === templateNamespace ? '|stub=yes' : ''); break; default: // ffd, rfd break; } notifytext += '|1=' + Morebits.pageNameNorm + '}} ~~~~'; return notifytext; }, addToLog: function(params, initialContrib) { if (!Twinkle.getPref('logXfdNominations') || Twinkle.getPref('noLogOnXfdNomination').includes(params.venue)) { return; } const usl = new Morebits.UserspaceLogger(Twinkle.getPref('xfdLogPageName'));// , 'Adding entry to userspace log'); usl.initialText = "This is a log of all [[WP:XFD|deletion discussion]] nominations made by this user using [[WP:TW|Twinkle]]'s XfD module.\n\n" + 'If you no longer wish to keep this log, you can turn it off using the [[Wikipedia:Twinkle/Preferences|preferences panel]], and ' + 'nominate this page for speedy deletion under [[WP:CSD#U1|CSD U1]].' + (Morebits.userIsSysop ? '\n\nThis log does not track XfD-related deletions made using Twinkle.' : ''); let editsummary; if (params.discussionpage) { editsummary = 'Logging [[' + params.discussionpage + '|' + utils.toTLACase(params.venue) + ' nomination]] of [[:' + Morebits.pageNameNorm + ']].'; } else { editsummary = 'Logging ' + utils.toTLACase(params.venue) + ' nomination of [[:' + Morebits.pageNameNorm + ']].'; } // If a logged file is deleted but exists on commons, the wikilink will be blue, so provide a link to the log const fileLogLink = mw.config.get('wgNamespaceNumber') === 6 ? ' ([{{fullurl:Special:Log|page=' + mw.util.wikiUrlencode(mw.config.get('wgPageName')) + '}} log])' : ''; // CFD/S and RM don't have canonical links const nominatedLink = params.discussionpage ? '[[' + params.discussionpage + '|nominated]]' : 'nominated'; let appendText = '# [[:' + Morebits.pageNameNorm + ']]:' + fileLogLink + ' ' + nominatedLink + ' at [[WP:' + params.venue.toUpperCase() + '|' + utils.toTLACase(params.venue) + ']]'; switch (params.venue) { case 'tfd': if (params.xfdcat === 'tfm') { appendText += ' (merge)'; if (params.tfdtarget) { const contentModel = mw.config.get('wgPageContentModel') === 'Scribunto' ? 'Module:' : 'Template:'; appendText += '; Other ' + contentModel.toLowerCase() + ' [['; if (!new RegExp('^:?' + Morebits.namespaceRegex([10, 828]) + ':', 'i').test(params.tfdtarget)) { appendText += contentModel; } appendText += params.tfdtarget + ']]'; } } break; case 'mfd': if (params.notifyuserspace && params.userspaceOwner && params.userspaceOwner !== initialContrib) { appendText += '; notified {{user|1=' + params.userspaceOwner + '}}'; } break; case 'cfd': appendText += ' (' + utils.toTLACase(params.xfdcat) + ')'; if (params.cfdtarget) { const categoryOrTemplate = params.xfdcat.charAt(0) === 's' ? 'Template:' : ':Category:'; appendText += '; ' + params.action + ' to [[' + categoryOrTemplate + params.cfdtarget + ']]'; if (params.xfdcat === 'cfs' && params.cfdtarget2) { appendText += ', [[' + categoryOrTemplate + params.cfdtarget2 + ']]'; } } break; case 'cfds': appendText += ' (' + utils.toTLACase(params.xfdcat) + ')'; // Ensure there's more than just 'Category:' if (params.cfdstarget && params.cfdstarget.length > 9) { appendText += '; New name: [[:' + params.cfdstarget + ']]'; } break; case 'rfd': if (params.rfdtarget) { appendText += '; Target: [[:' + params.rfdtarget + ']]'; if (params.relatedpage) { appendText += ' (notified)'; } } break; case 'rm': appendText = params.currentname .map((currentname, i) => `# [[:${currentname}]]: ${nominatedLink} at [[WP:RM${params.rmtr ? '/TR' : ''}|]]${params.newname[i] ? `; New name: [[:${params.newname[i]}]]` : ''}`) .join('\n'); break; default: // afd or ffd break; } if (initialContrib && params.notifycreator) { appendText += '; notified {{user|1=' + initialContrib + '}}'; } appendText += ' ~~~~~'; if (params.reason) { appendText += "\n#* '''Reason''': " + Morebits.string.formatReasonForLog(params.reason); } usl.changeTags = Twinkle.changeTags; usl.log(appendText, editsummary); }, afd: { main: function(apiobj) { const response = apiobj.getResponse(); const titles = response.query.allpages; // There has been no earlier entries with this prefix, just go on. if (titles.length <= 0) { apiobj.params.numbering = apiobj.params.number = ''; } else { let number = 0; for (let i = 0; i < titles.length; ++i) { const title = titles[i].title; // First, simple test, is there an instance with this exact name? if (title === 'Wikipedia:Articles for deletion/' + Morebits.pageNameNorm) { number = Math.max(number, 1); continue; } const order_re = new RegExp('^' + Morebits.string.escapeRegExp('Wikipedia:Articles for deletion/' + Morebits.pageNameNorm) + '\\s*\\(\\s*(\\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\\s*\\)\\s*$'); const match = order_re.exec(title); // No match; A non-good value // Or the match is an unrealistically high number. Avoid false positives such as Wikipedia:Articles for deletion/The Basement (2014), by ignoring matches greater than 100 if (!match || match[1] > 100) { continue; } // A match, set number to the max of current number = Math.max(number, Number(match[1])); } apiobj.params.number = utils.num2order(parseInt(number, 10) + 1); apiobj.params.numbering = number > 0 ? ' (' + apiobj.params.number + ' nomination)' : ''; } apiobj.params.discussionpage = 'Wikipedia:Articles for deletion/' + Morebits.pageNameNorm + apiobj.params.numbering; Morebits.Status.info('Next discussion page', '[[' + apiobj.params.discussionpage + ']]'); // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = apiobj.params.discussionpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Tagging article const wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Adding deletion tag to article'); wikipedia_page.setFollowRedirect(true); // should never be needed, but if the article is moved, we would want to follow the redirect wikipedia_page.setChangeTags(Twinkle.changeTags); // Here to apply to triage wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.afd.taggingArticle); }, // Tagging needs to happen before everything else: this means we can check if there is an AfD tag already on the page taggingArticle: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); if (!pageobj.exists()) { statelem.error("It seems that the page doesn't exist; perhaps it has already been deleted"); return; } // Check for existing AfD tag, for the benefit of new page patrollers const textNoAfd = text.replace(/<!--.*AfD.*\n\{\{(?:Article for deletion\/dated|AfDM).*\}\}\n<!--.*(?:\n<!--.*)?AfD.*(?:\s*\n)?/g, ''); if (text !== textNoAfd) { if (confirm('An AfD tag was found on this article. Maybe someone beat you to it. \nClick OK to replace the current AfD tag (not recommended), or Cancel to abandon your nomination.')) { text = textNoAfd; } else { statelem.error('Article already tagged with AfD tag, and you chose to abort'); window.location.reload(); return; } } // Now we know we want to go ahead with it, trigger the other AJAX requests // Mark the page as curated/patrolled, if wanted if (Twinkle.getPref('markXfdPagesAsPatrolled')) { pageobj.triage(); } // Start discussion page, will also handle pagetriage and delsort listings let wikipedia_page = new Morebits.wiki.Page(params.discussionpage, 'Creating article deletion discussion page'); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.afd.discussionPage); // Today's list const date = new Morebits.Date(pageobj.getLoadTime()); wikipedia_page = new Morebits.wiki.Page('Wikipedia:Articles for deletion/Log/' + date.format('YYYY MMMM D', 'utc'), "Adding discussion to today's list"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.afd.todaysList); // Notification to first contributor if (params.notifycreator) { const thispage = new Morebits.wiki.Page(mw.config.get('wgPageName')); thispage.setCallbackParameters(params); thispage.setLookupNonRedirectCreator(true); // Look for author of first non-redirect revision thispage.lookupCreation((pageobj) => { Twinkle.xfd.callbacks.notifyUser(pageobj.getCallbackParameters(), pageobj.getCreator()); }); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } // Add AFD tag to article params.tagText = Twinkle.xfd.callbacks.afd.generateArticleTagWikitext( params.noinclude, params.outcome, params.afdtarget, params.number ); // If the selected outcome is merge, add {{Merge from}} to the target page if (params.outcome === 'merging' && params.afdtarget) { wikipedia_page = new Morebits.wiki.Page(params.afdtarget, 'Tagging the target page with {{Merge from}}'); wikipedia_page.setCallbackParameters(params); wikipedia_page.setFollowRedirect(true); wikipedia_page.load(Twinkle.xfd.callbacks.afd.tagTargetPageWithMergeFromTag); } if (pageobj.canEdit()) { // Remove some tags that should always be removed on AfD. text = text.replace(/\{\{\s*(dated prod|dated prod blp|Prod blp\/dated|Proposed deletion\/dated|prod2|Proposed deletion endorsed|Userspace draft)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, ''); // Then, test if there are speedy deletion-related templates on the article. const textNoSd = text.replace(/\{\{\s*(db(-\w*)?|delete|(?:hang|hold)[- ]?on)\s*(\|(?:\{\{[^{}]*\}\}|[^{}])*)?\}\}\s*/ig, ''); if (text !== textNoSd && confirm('A speedy deletion tag was found on this page. Should it be removed?')) { text = textNoSd; } // Insert tag after short description or any hatnotes const wikipage = new Morebits.wikitext.Page(text); text = wikipage.insertAfterTemplates(params.tagText, Twinkle.hatnoteRegex).getText(); pageobj.setPageText(text); pageobj.setEditSummary(`Nominated for ${params.outcome}; see [[:${params.discussionpage}]].`); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('nocreate'); pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, tagTargetPageWithMergeFromTag: function(pageobj) { const statelem = pageobj.getStatusElement(); if (!pageobj.exists()) { statelem.warn('Failed. Target page not found.'); return; } else if (!pageobj.canEdit()) { statelem.warn('Failed. Target page is protected from editing.'); return; } const params = pageobj.getCallbackParameters(); const tag = `{{Merge from |1=${Morebits.pageNameNorm} |target=${params.afdtarget} |afd=${Morebits.pageNameNorm + params.numbering} |date ={{subst:CURRENTMONTHNAME}} {{subst:CURRENTYEAR}} }}`; const wikipage = new Morebits.wikitext.Page(pageobj.getPageText()); const text = wikipage.insertAfterTemplates(tag, Twinkle.hatnoteRegex).getText(); pageobj.setPageText(text); pageobj.setEditSummary('Nominated for merging; see [[:' + params.discussionpage + ']].'); pageobj.setCreateOption('nocreate'); pageobj.save(); }, generateArticleTagWikitext: function(noinclude, outcome, afdtarget, number) { let noIncludeStart = ''; let noIncludeEnd = ''; if (noinclude) { noIncludeStart = '<noinclude>'; noIncludeEnd = '</noinclude>'; } let templateAndParams = ''; const outcomeParam = outcome !== 'deletion' ? '|outcome=' + outcome : ''; const targetParam = afdtarget ? '|target=' + afdtarget : ''; const isFirstNomination = number === ''; if (isFirstNomination) { templateAndParams = 'subst:afd|help=off' + outcomeParam + targetParam; } else { templateAndParams = 'subst:afdx|' + number + '|help=off' + outcomeParam + targetParam; } return noIncludeStart + '{{' + templateAndParams + '}}' + noIncludeEnd + '\n'; }, discussionPage: function(pageobj) { const params = pageobj.getCallbackParameters(); pageobj.setPageText(Twinkle.xfd.callbacks.getDiscussionWikitext('afd', params)); pageobj.setEditSummary('Creating AfD discussion page for [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('createonly'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki // Actions that should wait on the discussion page actually being created // and whose errors shouldn't output the user rationale // List at deletion sorting pages if (params.delsortCats) { params.delsortCats.forEach((cat) => { const delsortPage = new Morebits.wiki.Page('Wikipedia:WikiProject Deletion sorting/' + cat, 'Adding to list of ' + cat + '-related deletion discussions'); delsortPage.setFollowRedirect(true); // In case a category gets renamed delsortPage.setCallbackParameters({discussionPage: params.discussionpage}); delsortPage.load(Twinkle.xfd.callbacks.afd.delsortListing); }); } }); }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = '{{subst:afd3|pg=' + Morebits.pageNameNorm + params.numbering + '}}\n'; let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:AfD log}}\n' + added_data; } else { const old_text = pageobj.getPageText() + '\n'; // MW strips trailing blanks, but we like them, so we add a fake one text = old_text.replace(/(<!-- Add new entries to the TOP of the following list -->\n+)/, '$1' + added_data); if (text === old_text) { const linknode = document.createElement('a'); linknode.setAttribute('href', mw.util.getUrl('Wikipedia:Twinkle/Fixing AFD') + '?action=purge'); linknode.appendChild(document.createTextNode('How to fix AFD')); statelem.error(['Could not find the target spot for the discussion. To fix this problem, please see ', linknode, '.']); return; } } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchList')); pageobj.setCreateOption('recreate'); pageobj.setDiscussionToolsAutoSubscribe(false); pageobj.save(); }, delsortListing: function(pageobj) { const discussionPage = pageobj.getCallbackParameters().discussionPage; const text = pageobj.getPageText().replace('directly below this line -->', 'directly below this line -->\n{{' + discussionPage + '}}'); pageobj.setPageText(text); pageobj.setEditSummary('Listing [[:' + discussionPage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('nocreate'); pageobj.setDiscussionToolsAutoSubscribe(false); pageobj.save(); } }, tfd: { main: function(pageobj) { const params = pageobj.getCallbackParameters(); const date = new Morebits.Date(pageobj.getLoadTime()); params.logpage = 'Wikipedia:Templates for discussion/Log/' + date.format('YYYY MMMM D', 'utc'); params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; // Add log/discussion page params to the already-loaded page object pageobj.setCallbackParameters(params); // Defined here rather than below to reduce duplication let watchModule, watch_query; if (params.scribunto) { const watchPref = Twinkle.getPref('xfdWatchPage'); // action=watch has no way to rely on user // preferences (T262912), so we do it manually. // The watchdefault pref appears to reliably return '1' (string), // but that's not consistent among prefs so might as well be "correct" watchModule = watchPref !== 'no' && (watchPref !== 'default' || !!parseInt(mw.user.options.get('watchdefault'), 10)); if (watchModule) { watch_query = { action: 'watch', titles: [mw.config.get('wgPageName')], token: mw.user.tokens.get('watchToken') }; // Only add the expiry if page is unwatched or already temporarily watched if (pageobj.getWatched() !== true && watchPref !== 'default' && watchPref !== 'yes') { watch_query.expiry = watchPref; } } } // Tagging template(s)/module(s) if (params.xfdcat === 'tfm') { // Merge let wikipedia_otherpage; if (params.scribunto) { wikipedia_otherpage = new Morebits.wiki.Page(params.otherTemplateName + '/doc', 'Tagging other module documentation with merge tag'); // Watch tagged module pages as well if (watchModule) { watch_query.titles.push(params.otherTemplateName); new Morebits.wiki.Api('Adding Modules to watchlist', watch_query).post(); } } else { wikipedia_otherpage = new Morebits.wiki.Page(params.otherTemplateName, 'Tagging other template with merge tag'); } // Tag this template/module Twinkle.xfd.callbacks.tfd.taggingTemplateForMerge(pageobj); // Tag other template/module wikipedia_otherpage.setFollowRedirect(true); const otherParams = $.extend({}, params); otherParams.otherTemplateName = Morebits.pageNameNorm; wikipedia_otherpage.setCallbackParameters(otherParams); wikipedia_otherpage.load(Twinkle.xfd.callbacks.tfd.taggingTemplateForMerge); } else { // delete if (params.scribunto && Twinkle.getPref('xfdWatchPage') !== 'no') { // Watch tagged module page as well if (watchModule) { new Morebits.wiki.Api('Adding Module to watchlist', watch_query).post(); } } Twinkle.xfd.callbacks.tfd.taggingTemplate(pageobj); } // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log"; // Adding discussion const wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's log"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.tfd.todaysList); // Notification to first contributors if (params.notifycreator) { const involvedpages = []; const seenusers = []; involvedpages.push(new Morebits.wiki.Page(mw.config.get('wgPageName'))); if (params.xfdcat === 'tfm') { if (params.scribunto) { involvedpages.push(new Morebits.wiki.Page('Module:' + params.tfdtarget)); } else { involvedpages.push(new Morebits.wiki.Page('Template:' + params.tfdtarget)); } } involvedpages.forEach((page) => { page.setCallbackParameters(params); page.lookupCreation((innerpage) => { const username = innerpage.getCreator(); if (!seenusers.includes(username)) { seenusers.push(username); // Only log once on merge nominations, for the initial template Twinkle.xfd.callbacks.notifyUser(innerpage.getCallbackParameters(), username, params.xfdcat === 'tfm' && innerpage.getPageName() !== Morebits.pageNameNorm); } }); }); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } // Notify developer(s) of script(s) that use(s) the nominated template if (params.devpages) { const inCategories = mw.config.get('wgCategories'); const categoryNotificationPageMap = { 'Templates used by Twinkle': 'Wikipedia talk:Twinkle', 'Templates used by AutoWikiBrowser': 'Wikipedia talk:AutoWikiBrowser', 'Templates used by Ultraviolet': 'Wikipedia talk:Ultraviolet' }; $.each(categoryNotificationPageMap, (category, page) => { if (inCategories.includes(category)) { Twinkle.xfd.callbacks.notifyUser(params, page, true, 'Notifying ' + page + ' of template nomination'); } }); } }, taggingTemplate: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{subst:template for discussion|help=off' + (params.templatetype !== 'standard' ? '|type=' + params.templatetype : '') + '}}'; if (pageobj.getContentModel() === 'sanitized-css') { params.tagText = '/* ' + params.tagText + ' */'; } else { if (params.noinclude) { params.tagText = '<noinclude>' + params.tagText + '</noinclude>'; } params.tagText += params.templatetype === 'standard' || params.templatetype === 'sidebar' || params.templatetype === 'disabled' ? '\n' : ''; // No newline for inline } if (pageobj.canEdit() && ['wikitext', 'sanitized-css'].includes(pageobj.getContentModel())) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Nominated for deletion; see [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); if (params.scribunto) { pageobj.setCreateOption('recreate'); // Module /doc might not exist } pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, taggingTemplateForMerge: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{subst:tfm|help=off|' + (params.templatetype !== 'standard' ? 'type=' + params.templatetype + '|' : '') + '1=' + params.otherTemplateName.replace(new RegExp('^' + Morebits.namespaceRegex([10, 828]) + ':'), '') + '}}'; if (pageobj.getContentModel() === 'sanitized-css') { params.tagText = '/* ' + params.tagText + ' */'; } else { if (params.noinclude) { params.tagText = '<noinclude>' + params.tagText + '</noinclude>'; } params.tagText += params.templatetype === 'standard' || params.templatetype === 'sidebar' || params.templatetype === 'disabled' ? '\n' : ''; // No newline for inline } if (pageobj.canEdit() && ['wikitext', 'sanitized-css'].includes(pageobj.getContentModel())) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Listed for merging with [[:' + params.otherTemplateName + ']]; see [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); if (params.scribunto) { pageobj.setCreateOption('recreate'); // Module /doc might not exist } pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = Twinkle.xfd.callbacks.getDiscussionWikitext(params.xfdcat, params); let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:TfD log}}\n' + added_data; } else { const old_text = pageobj.getPageText(); text = old_text.replace('-->', '-->\n' + added_data); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } } pageobj.setPageText(text); pageobj.setEditSummary('/* ' + Morebits.pageNameNorm + ' */ Adding ' + (params.xfdcat === 'tfd' ? 'deletion nomination' : 'merge listing') + ' of [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, mfd: { main: function(apiobj) { const response = apiobj.getResponse(); const titles = response.query.allpages; // There has been no earlier entries with this prefix, just go on. if (titles.length <= 0) { apiobj.params.numbering = apiobj.params.number = ''; } else { let number = 0; for (let i = 0; i < titles.length; ++i) { const title = titles[i].title; // First, simple test, is there an instance with this exact name? if (title === 'Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm) { number = Math.max(number, 1); continue; } const order_re = new RegExp('^' + Morebits.string.escapeRegExp('Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm) + '\\s*\\(\\s*(\\d+)(?:(?:th|nd|rd|st) nom(?:ination)?)?\\s*\\)\\s*$'); const match = order_re.exec(title); // No match; A non-good value if (!match) { continue; } // A match, set number to the max of current number = Math.max(number, Number(match[1])); } apiobj.params.number = utils.num2order(parseInt(number, 10) + 1); apiobj.params.numbering = number > 0 ? ' (' + apiobj.params.number + ' nomination)' : ''; } apiobj.params.discussionpage = 'Wikipedia:Miscellany for deletion/' + Morebits.pageNameNorm + apiobj.params.numbering; apiobj.statelem.info('next in order is [[' + apiobj.params.discussionpage + ']]'); let wikipedia_page; // Tagging page if (mw.config.get('wgNamespaceNumber') !== 710) { // cannot tag TimedText pages wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging page with deletion tag'); wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.mfd.taggingPage); } // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = apiobj.params.discussionpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Discussion page wikipedia_page = new Morebits.wiki.Page(apiobj.params.discussionpage, 'Creating deletion discussion page'); wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.mfd.discussionPage); // Today's list wikipedia_page = new Morebits.wiki.Page('Wikipedia:Miscellany for deletion', "Adding discussion to today's list"); wikipedia_page.setPageSection(2); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(apiobj.params); wikipedia_page.load(Twinkle.xfd.callbacks.mfd.todaysList); // Notification to first contributor and/or notification to owner of userspace if (apiobj.params.notifycreator || apiobj.params.notifyuserspace) { const thispage = new Morebits.wiki.Page(mw.config.get('wgPageName')); thispage.setCallbackParameters(apiobj.params); thispage.lookupCreation(Twinkle.xfd.callbacks.mfd.sendNotifications); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(apiobj.params, null); } }, taggingPage: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{' + (params.number === '' ? 'mfd' : 'mfdx|' + params.number) + '|help=off}}'; if (['javascript', 'css', 'sanitized-css'].includes(mw.config.get('wgPageContentModel'))) { params.tagText = '/* ' + params.tagText + ' */\n'; } else { params.tagText += '\n'; if (params.noinclude) { params.tagText = '<noinclude>' + params.tagText + '</noinclude>'; } } if (pageobj.canEdit() && ['wikitext', 'javascript', 'css', 'sanitized-css'].includes(pageobj.getContentModel())) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Nominated for deletion; see [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('nocreate'); pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, discussionPage: function(pageobj) { const params = pageobj.getCallbackParameters(); pageobj.setPageText(Twinkle.xfd.callbacks.getDiscussionWikitext('mfd', params)); pageobj.setEditSummary('Creating deletion discussion page for [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('createonly'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); }, todaysList: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const date = new Morebits.Date(pageobj.getLoadTime()); const date_header = date.format('===MMMM D, YYYY===\n', 'utc'); const date_header_regex = new RegExp(date.format('(===[\\s]*MMMM[\\s]+D,[\\s]+YYYY[\\s]*===)', 'utc')); const added_data = '{{subst:mfd3|pg=' + Morebits.pageNameNorm + params.numbering + '}}'; if (date_header_regex.test(text)) { // we have a section already statelem.info('Found today\'s section, proceeding to add new entry'); text = text.replace(date_header_regex, '$1\n' + added_data); } else { // we need to create a new section statelem.info('No section for today found, proceeding to create one'); text = text.replace('===', date_header + added_data + '\n\n==='); } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchList')); pageobj.setCreateOption('recreate'); pageobj.setDiscussionToolsAutoSubscribe(false); pageobj.save(); }, sendNotifications: function(pageobj) { const initialContrib = pageobj.getCreator(); const params = pageobj.getCallbackParameters(); // Notify the creator if (params.notifycreator) { Twinkle.xfd.callbacks.notifyUser(params, initialContrib); } // Notify the user who owns the subpage if they are not the creator params.userspaceOwner = mw.config.get('wgRelevantUserName'); if (params.notifyuserspace) { if (params.userspaceOwner !== initialContrib) { // Don't log if notifying creator above, will log then Twinkle.xfd.callbacks.notifyUser(params, params.userspaceOwner, params.notifycreator, 'Notifying owner of userspace (' + params.userspaceOwner + ')'); } else if (!params.notifycreator) { // If we thought we would notify the owner but didn't, // then we need to log if we didn't notify the creator // Twinkle.xfd.callbacks.addToLog(params, null); Twinkle.xfd.callbacks.addToLog(params, initialContrib); } } } }, ffd: { taggingImage: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const date = new Morebits.Date(pageobj.getLoadTime()).format('YYYY MMMM D', 'utc'); params.logpage = 'Wikipedia:Files for discussion/' + date; params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; params.tagText = '{{ffd|log=' + date + '|help=off}}\n'; if (pageobj.canEdit()) { text = Twinkle.removeMoveToCommonsTagsFromWikicode(text); pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Listed for discussion at [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('recreate'); // it might be possible for a file to exist without a description page pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Contributor specific edits const wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName')); wikipedia_page.setCallbackParameters(params); wikipedia_page.lookupCreation(Twinkle.xfd.callbacks.ffd.main); }, main: function(pageobj) { // this is coming in from lookupCreation...! const params = pageobj.getCallbackParameters(); const initialContrib = pageobj.getCreator(); params.uploader = initialContrib; // Adding discussion const wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's list"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.ffd.todaysList); // Notification to first contributor if (params.notifycreator) { Twinkle.xfd.callbacks.notifyUser(params, initialContrib); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } }, todaysList: function(pageobj) { let text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:FfD log}}'; } pageobj.setPageText(text + '\n\n' + Twinkle.xfd.callbacks.getDiscussionWikitext('ffd', params)); pageobj.setEditSummary('Adding [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, cfd: { main: function(pageobj) { const params = pageobj.getCallbackParameters(); const date = new Morebits.Date(pageobj.getLoadTime()); params.logpage = 'Wikipedia:Categories for discussion/Log/' + date.format('YYYY MMMM D', 'utc'); params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; // Add log/discussion page params to the already-loaded page object pageobj.setCallbackParameters(params); // Tagging category Twinkle.xfd.callbacks.cfd.taggingCategory(pageobj); // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log"; // Adding discussion to list let wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's list"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfd.todaysList); // Notification to first contributor if (params.notifycreator) { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName')); wikipedia_page.setCallbackParameters(params); wikipedia_page.lookupCreation((pageobj) => { Twinkle.xfd.callbacks.notifyUser(pageobj.getCallbackParameters(), pageobj.getCreator()); }); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } }, taggingCategory: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); params.tagText = '{{subst:' + params.xfdcat; let editsummary = (mw.config.get('wgNamespaceNumber') === 14 ? 'Category' : 'Stub template') + ' being considered for ' + params.action; switch (params.xfdcat) { case 'cfd': case 'sfd-t': break; case 'cfc': editsummary += ' to an article'; // falls through case 'cfm': case 'cfr': case 'sfr-t': params.tagText += '|' + params.cfdtarget; break; case 'cfs': params.tagText += '|' + params.cfdtarget + '|' + params.cfdtarget2; break; default: alert('twinklexfd in taggingCategory(): unknown CFD action'); break; } params.tagText += '}}\n'; editsummary += '; see [[:' + params.discussionpage + ']].'; if (pageobj.canEdit()) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary(editsummary); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('recreate'); // since categories can be populated without an actual page at that title pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = Twinkle.xfd.callbacks.getDiscussionWikitext(params.xfdcat, params); let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:CfD log}}\n' + added_data; } else { const old_text = pageobj.getPageText(); text = old_text.replace('below this line -->', 'below this line -->\n' + added_data); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } } pageobj.setPageText(text); pageobj.setEditSummary('Adding ' + params.action + ' nomination of [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, cfds: { taggingCategory: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); if (params.xfdcat === 'C2F') { params.tagText = '{{subst:cfm-speedy|1=' + params.cfdstarget.replace(/^:?Category:/, '') + '}}\n'; } else { params.tagText = '{{subst:cfr-speedy|1=' + params.cfdstarget.replace(/^:?Category:/, '') + '}}\n'; } params.discussionpage = ''; // CFDS is just a bullet in a bulleted list. There's no section to link to, so we set this to blank. Blank will be recognized by both the generate userspace log code and the generate userspace log edit summary code as "don't wikilink to a section". if (pageobj.canEdit()) { pageobj.setPageText(params.tagText + text); pageobj.setEditSummary('Listed for speedy renaming; see [[WP:CFDS|Categories for discussion/Speedy]].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('recreate'); // since categories can be populated without an actual page at that title pageobj.save(() => { // No user notification for CfDS, so just add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); }); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); // No user notification for CfDS, so just add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); } }, addToList: function(pageobj) { const old_text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const text = old_text.replace('BELOW THIS LINE -->', 'BELOW THIS LINE -->\n' + Twinkle.xfd.callbacks.getDiscussionWikitext('cfds', params)); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); } }, rfd: { // This gets called both on submit and preview to determine the redirect target findTarget: function(params, callback) { // Used by regular redirects to find the target, but for all redirects, // avoid relying on the client clock to build the log page const query = { action: 'query', curtimestamp: true, format: 'json' }; if (document.getElementById('softredirect')) { // For soft redirects, define the target early // to skip target checks in findTargetCallback params.rfdtarget = document.getElementById('softredirect').textContent.replace(/^:+/, ''); } else { // Find current target of redirect query.titles = mw.config.get('wgPageName'); query.redirects = true; } const wikipedia_api = new Morebits.wiki.Api('Finding target of redirect', query, Twinkle.xfd.callbacks.rfd.findTargetCallback(callback)); wikipedia_api.params = params; wikipedia_api.post(); }, // This is a closure for the callback from the above API request, which gets the target of the redirect findTargetCallback: function(callback) { return function(apiobj) { const response = apiobj.getResponse(); apiobj.params.curtimestamp = response.curtimestamp; if (!apiobj.params.rfdtarget) { // Not a softredirect const target = response.query.redirects && response.query.redirects[0].to; if (!target) { let message = 'No target found. this page does not appear to be a redirect, aborting'; if (mw.config.get('wgAction') === 'history') { message += '. If this is a soft redirect, try again from the content page, not the page history.'; } apiobj.statelem.error(message); return; } apiobj.params.rfdtarget = target; const section = response.query.redirects[0].tofragment; apiobj.params.section = section; } callback(apiobj.params); }; }, main: function(params) { const date = new Morebits.Date(params.curtimestamp); params.logpage = 'Wikipedia:Redirects for discussion/Log/' + date.format('YYYY MMMM D', 'utc'); params.discussionpage = params.logpage + '#' + Morebits.pageNameNorm; // Tagging redirect let wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Adding deletion tag to redirect'); wikipedia_page.setFollowRedirect(false); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.rfd.taggingRedirect); // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = params.logpage; Morebits.wiki.actionCompleted.notice = "Nomination completed, now redirecting to today's log"; // Adding discussion wikipedia_page = new Morebits.wiki.Page(params.logpage, "Adding discussion to today's log"); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.rfd.todaysList); // Notifications if (params.notifycreator || params.relatedpage) { const thispage = new Morebits.wiki.Page(mw.config.get('wgPageName')); thispage.setCallbackParameters(params); thispage.lookupCreation(Twinkle.xfd.callbacks.rfd.sendNotifications); // or, if not notifying, add this nomination to the user's userspace log without the initial contributor's name } else { Twinkle.xfd.callbacks.addToLog(params, null); } }, taggingRedirect: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); // Imperfect for edit request but so be it params.tagText = '{{subst:rfd|' + (mw.config.get('wgNamespaceNumber') === 10 ? 'showontransclusion=1|' : '') + 'content=\n'; if (pageobj.canEdit()) { pageobj.setPageText(params.tagText + text + '\n}}'); pageobj.setEditSummary('Listed for discussion at [[:' + params.discussionpage + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchPage')); pageobj.setCreateOption('nocreate'); pageobj.save(); } else { Twinkle.xfd.callbacks.autoEditRequest(pageobj, params); } }, todaysList: function(pageobj) { const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const added_data = Twinkle.xfd.callbacks.getDiscussionWikitext('rfd', params); let text; // add date header if the log is found to be empty (a bot should do this automatically) if (!pageobj.exists()) { text = '{{subst:RfD log}}' + added_data; } else { const old_text = pageobj.getPageText(); text = old_text.replace(/(<!-- Add new entries directly below this line\.? -->)/, '$1\n' + added_data); if (text === old_text) { statelem.error('failed to find target spot for the discussion'); return; } } pageobj.setPageText(text); pageobj.setEditSummary('Adding [[:' + Morebits.pageNameNorm + ']].'); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.setCreateOption('recreate'); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki }); }, sendNotifications: function(pageobj) { const initialContrib = pageobj.getCreator(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); // Notifying initial contributor if (params.notifycreator) { Twinkle.xfd.callbacks.notifyUser(params, initialContrib); } // Notifying target page's watchers, if not a soft redirect if (params.relatedpage) { const targetTalk = new mw.Title(params.rfdtarget).getTalkPage(); // On the offchance it's a circular redirect if (params.rfdtarget === mw.config.get('wgPageName')) { statelem.warn('Circular redirect; skipping target page notification'); } else if (document.getElementById('softredirect')) { statelem.warn('Soft redirect; skipping target page notification'); // Don't issue if target talk is the initial contributor's talk or your own } else if (targetTalk.getNamespaceId() === 3 && targetTalk.getMainText() === initialContrib) { statelem.warn('Target is initial contributor; skipping target page notification'); } else if (targetTalk.getNamespaceId() === 3 && targetTalk.getMainText() === mw.config.get('wgUserName')) { statelem.warn('You (' + mw.config.get('wgUserName') + ') are the target; skipping target page notification'); } else { // Don't log if notifying creator above, will log then Twinkle.xfd.callbacks.notifyUser(params, targetTalk.toText(), params.notifycreator, 'Notifying redirect target of the discussion'); return; } // If we thought we would notify the target but didn't, // we need to log if we didn't notify the creator if (!params.notifycreator) { Twinkle.xfd.callbacks.addToLog(params, null); } } } }, rm: { listAtTalk: function(pageobj) { const params = pageobj.getCallbackParameters(); params.discussionpage = pageobj.getPageName(); pageobj.setAppendText('\n\n' + Twinkle.xfd.callbacks.getDiscussionWikitext('rm', params)); pageobj.setEditSummary(`Proposing move of ${ params.currentname .map((currentname, i) => `[[:${currentname}]]${params.newname[i] ? ` to [[:${params.newname[i]}]]` : ''}`) .join(', ') }.`); pageobj.setChangeTags(Twinkle.changeTags); pageobj.setCreateOption('recreate'); // since the talk page need not exist pageobj.setWatchlist(Twinkle.getPref('xfdWatchDiscussion')); pageobj.append(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki // add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); }); }, listAtRMTR: function(pageobj) { const text = pageobj.getPageText(); const params = pageobj.getCallbackParameters(); const statelem = pageobj.getStatusElement(); const discussionWikitext = Twinkle.xfd.callbacks.getDiscussionWikitext('rm', params); const newtext = Twinkle.xfd.insertRMTR(text, discussionWikitext); if (text === newtext) { statelem.error('failed to find target spot for the entry'); return; } pageobj.setPageText(newtext); pageobj.setEditSummary(`Adding [[:${params.currentname.join(']], [[:')}]].`); pageobj.setChangeTags(Twinkle.changeTags); pageobj.save(() => { Twinkle.xfd.currentRationale = null; // any errors from now on do not need to print the rationale, as it is safely saved on-wiki // add this nomination to the user's userspace log Twinkle.xfd.callbacks.addToLog(params, null); }); } } }; /** * Given the wikitext of the WP:RM/TR page and the wikitext to insert, insert it at the bottom of the ==== Uncontroversial technical requests ==== section. * * @param {string} pageWikitext * @param {string} wikitextToInsert Will typically be `{{subst:RMassist|1=From|2=To|reason=Reason}}`, which expands out to `* {{RMassist/core | 1 = From | 2 = To | discuss = yes | reason = Reason | sig = Signature | requester = YourUserName}}` * @return {string} pageWikitext */ Twinkle.xfd.insertRMTR = function(pageWikitext, wikitextToInsert) { // [^\n]* is for matching anchors, e.g. <span class="anchor" id="*"></span> const placementRE = /\n{1,}(====[^\n]*Requests to revert undiscussed moves ?====)/i; return pageWikitext.replace(placementRE, '\n' + wikitextToInsert + '\n\n$1'); }; Twinkle.xfd.callback.evaluate = function(e) { const form = e.target; const params = Morebits.QuickForm.getInputData(form); Morebits.SimpleWindow.setButtonsEnabled(false); Morebits.Status.init(form); Twinkle.xfd.currentRationale = params.reason; Morebits.Status.onError(Twinkle.xfd.printRationale); let query, wikipedia_page, wikipedia_api; switch (params.venue) { case 'afd': // AFD query = { action: 'query', list: 'allpages', apprefix: 'Articles for deletion/' + Morebits.pageNameNorm, apnamespace: 4, apfilterredir: 'nonredirects', aplimit: 'max', // 500 is max for normal users, 5000 for bots and sysops format: 'json' }; wikipedia_api = new Morebits.wiki.Api('Tagging article with deletion tag', query, Twinkle.xfd.callbacks.afd.main); wikipedia_api.params = params; wikipedia_api.post(); break; case 'tfd': // TFD if (params.tfdtarget) { // remove namespace name params.tfdtarget = utils.stripNs(params.tfdtarget); } // Modules can't be tagged, TfD instructions are to place on /doc subpage params.scribunto = mw.config.get('wgPageContentModel') === 'Scribunto'; if (params.xfdcat === 'tfm') { // Merge // Tag this template/module if (params.scribunto) { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName') + '/doc', 'Tagging this module documentation with merge tag'); params.otherTemplateName = 'Module:' + params.tfdtarget; } else { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging this template with merge tag'); params.otherTemplateName = 'Template:' + params.tfdtarget; } } else { // delete if (params.scribunto) { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName') + '/doc', 'Tagging module documentation with deletion tag'); } else { wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging template with deletion tag'); } } wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.tfd.main); break; case 'mfd': // MFD query = { action: 'query', list: 'allpages', apprefix: 'Miscellany for deletion/' + Morebits.pageNameNorm, apnamespace: 4, apfilterredir: 'nonredirects', aplimit: 'max', // 500 is max for normal users, 5000 for bots and sysops format: 'json' }; wikipedia_api = new Morebits.wiki.Api('Looking for prior nominations of this page', query, Twinkle.xfd.callbacks.mfd.main); wikipedia_api.params = params; wikipedia_api.post(); break; case 'ffd': // FFD // Tagging file // A little out of order with this coming before 'main', // but tagging doesn't need the uploader parameter, // while everything else does, so tag then get the uploader wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Adding deletion tag to file page'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.ffd.taggingImage); break; case 'cfd': if (params.cfdtarget) { params.cfdtarget = utils.stripNs(params.cfdtarget); } else { params.cfdtarget = ''; // delete } if (params.cfdtarget2) { // split params.cfdtarget2 = utils.stripNs(params.cfdtarget2); } // Used for customized actions in edit summaries and the notification template var summaryActions = { cfd: 'deletion', 'sfd-t': 'deletion', cfm: 'merging', cfr: 'renaming', 'sfr-t': 'renaming', cfs: 'splitting', cfc: 'conversion' }; params.action = summaryActions[params.xfdcat]; // Tagging category wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging category with ' + params.action + ' tag'); wikipedia_page.setFollowRedirect(true); // should never be needed, but if the page is moved, we would want to follow the redirect wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfd.main); break; case 'cfds': // add namespace name if missing params.cfdstarget = utils.addNs(params.cfdstarget, 14); var logpage = 'Wikipedia:Categories for discussion/Speedy'; // Updating data for the action completed event Morebits.wiki.actionCompleted.redirect = logpage; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; // Tagging category wikipedia_page = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging category with rename tag'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfds.taggingCategory); // Adding discussion to list wikipedia_page = new Morebits.wiki.Page(logpage, 'Adding discussion to the list'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); wikipedia_page.load(Twinkle.xfd.callbacks.cfds.addToList); break; case 'rfd': // find target and pass main as the callback Twinkle.xfd.callbacks.rfd.findTarget(params, Twinkle.xfd.callbacks.rfd.main); break; case 'rm': var nomPageName = params.rmtr ? 'Wikipedia:Requested moves/Technical requests' : new mw.Title(Morebits.pageNameNorm).getTalkPage().toText(); Morebits.wiki.actionCompleted.redirect = nomPageName; Morebits.wiki.actionCompleted.notice = 'Nomination completed, now redirecting to the discussion page'; wikipedia_page = new Morebits.wiki.Page(nomPageName, params.rmtr ? 'Adding entry at WP:RM/TR' : 'Adding entry on talk page'); wikipedia_page.setFollowRedirect(true); wikipedia_page.setCallbackParameters(params); if (params.rmtr) { wikipedia_page.load(Twinkle.xfd.callbacks.rm.listAtRMTR); } else { // listAtTalk uses .append(), so no need to load the page Twinkle.xfd.callbacks.rm.listAtTalk(wikipedia_page); } break; default: alert('twinklexfd: unknown XFD discussion venue'); break; } }; Twinkle.addInitCallback(Twinkle.xfd, 'xfd'); }()); // </nowiki> nbftc26fftb464h06xeih9vufaapuhu ವರಿಜಾಶ್ರೀ ವೇಣುಗೋಪಾಲ್ 0 26258 366105 357413 2026-08-21T02:45:23Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366105 wikitext text/x-wiki   '''ವರಿಜಾಶ್ರೀ ವೇಣುಗೋಪಾಲ್''', ಭಾರತದ [[ಬೆಂಗಳೂರು|ಬೆಂಗಳೂರುಡ್]] 6 ಮಾರ್ಚ್ 1991ಡ್ ಪುಟ್ಟುವೆರ್, ಒರ್ತಿ ಗ್ರ್ಯಾಮಿ ನಾಮನಿರ್ದೇಶನ ಆಯಿನ ಭಾರತೀಯ ಗಾಯಕಿ ಬೊಕ್ಕ ಫ್ಲೂಟಿಸ್ಟ್. <ref name=":0">{{Cite news|url=http://timesofindia.indiatimes.com/entertainment/kannada/music/Witness-the-Chakrafonics-live-in-concert/articleshow/46118496.cms|title=Witness the Chakrafonics live in concert – Times of India|date=4 February 2015|work=[[The Times of India]]}}</ref> 2024ಡ್ ಆರ್ ಜಾಕೋಬ್ ಕೊಲಿಯರ್ ಬೊಕ್ಕ ಅನೌಷ್ಕಾ ಶಂಕರ್ ನಕುಲೆನೊಟ್ಟುಗು ಎ ರಾಕ್ ಸಮ್ಹೆರ್ ಪನ್ಪಿನ ಕೆಲಸೊಗು ತನ್ನ ಸುರುತ ಗ್ರ್ಯಾಮಿ ನಾಮನಿರ್ದೇಶನೊನು ಪಡೆಯೆರ್. <ref>{{Cite news|url=https://thebetterindia.com/382543/varijashree-venugopal-grammy-award-nomination-a-rock-somehwere-jacob-collier-anoushka-shankar-bengaluru/|title=Varijashree gets her first Grammy nomination}}</ref> ಚಾರ್ಕಾಫೋನಿಕ್ಸ್, ಇವೈಎಂ ಟ್ರೈಯೊ, ಸ್ನಾರ್ಕಿ ಪಪ್ಪಿ ಇಂಚಿನ ಬ್ಯಾಂಡ್ ಲೆನೊಟ್ಟುಗು ಒಂಜಿ ಸ್ವತಂತ್ರ ಕಲಾವಿದೆ ಆದ್ ಕೆಲಸ ಮಲ್ಪುವೆರ್ ಬೊಕ್ಕ ಕರ್ನಾಟಿಕ್ ಸಂಗೀತ ಬೊಕ್ಕ ಜಾಝ್ ದ ಮಿಶ್ರಣ ಆಯಿನ "ಕಾರ್ನಾಟಿಕ್ ಸ್ಕ್ಯಾಟ್ ಸಿಂಗಿಂಗ್" ಪನ್ಪಿನ ಕಲಾ ರೂಪೊದ ಸೃಷ್ಟಿಕರ್ತೆರ್. == ಸುರುತ ಜೀವನ == ವರಿಜಾಶ್ರೀ ಎಚ್ ಎಸ್ ವೇಣುಗೋಪಾಲ್ ಬೊಕ್ಕ ಟಿವಿ ರಾಮ ರಡ್ಡ್ ಜನಲಾ ನಿಪುಣ ಸಂಗೀತಗಾರೆರ್ನ ಒಂಜಿ ಭಾರತೀಯ ಬ್ರಾಹ್ಮಣ ಕುಟುಂಬೊಡು ಪುಟ್ಟುವೆರ್. ಒಂಜಿ ಅರ್ಧ ವರ್ಸೊದ ಪ್ರಾಯೊಡು ಸುಮಾರ್ 40 ರಾಗೊಲೆನ್ ಗುರುತಿಸವುನ ಅಪರೂಪದ ಸಾಮರ್ಥ್ಯೊನು ಬೊಕ್ಕ 4 ವರ್ಸೊದ ಪ್ರಾಯೊಡು ಸುಮಾರ್ 200 ರಾಗೊಲೆನ್ ಗುರುತಿಸವುನ ಅಪರೂಪದ ಸಾಮರ್ಥ್ಯೊನು ತೆರಿಂಡ್ <ref>{{Cite news|url=https://timesofindia.indiatimes.com/entertainment/kannada/music/music-was-the-first-language-i-knew-varijashree-venugopal/articleshow/107148540.cms|title=Music was the first language I knew: Varijashree Venugopal- Times of India|work=[[The Times of India]]}}</ref> ಆರ್ ಸುರುಕು ಅಪ್ಪೆನ ಕೈತಲ್ ತರಬೇತಿ ಪಡೆಯೆರ್ ಬೊಕ್ಕ 4 ವರ್ಸೊದ ಪ್ರಾಯೊಡು ವಿಧುಶಿ ಎಚ್. ಗೀತಾ ನ ಅಡಿಟ್ ಕರ್ನಾಟಕ ಸಂಗೀತೊಡು ಔಪಚಾರಿಕ ತರಬೇತಿನ್ ಪಡೆಯೆರೆ ಸುರು <ref>{{Cite news|url=https://thebetterindia.com/382543/varijashree-venugopal-grammy-award-nomination-a-rock-somehwere-jacob-collier-anoushka-shankar-bengaluru/|title=How a Love for Ragas as a Baby Led This Musical Prodigy to a Grammy Nomination|date=26 November 2024|work=[[The Better India]]}}</ref> ನನೊಂಜಿ ಸರ್ತಿ ಆರ್ ಒಂಜಿ ಅಗತ್ಯವಾಯಿನ ಸಂಯೋಜನೆ ತರಗತಿಲೆನ್ ಕಲ್ಪುವೆರ್. ವಿಧುಶಿ ವಸಂತ ಶ್ರೀನಿವಾಸನ್ ಮತ್ತು ವಿದ್ವಾನ್ ಡಿ ಎಸ್ ಶ್ರೀವತ್ಸರಿಂದ ವಿದ್ವಾತ್ ( ಹಿರಿಯ ಮಟ್ಟದ ಪ್ರದರ್ಶನ ಕಲಾತ್ಮಕ ತರಬೇತಿ ) . ಅರೆನ ಸಂಗೀತೊನು ಅರೆನ ಗುರು ಗಾನಕಲಾನಿಧಿ ವಿದ್ವಾನ್ ಸಲೀಂ ಪಿ. ಸುಂದರೇಸನ್ ಮಸ್ತ್ ರೂಪೊ ಕೊರ್ತೆರ್. ಅಲೆನ ಅಪ್ಪೆ ವಿಡ್ ನ ಒಟ್ಟುಗು ಆರ್ ಒರಿ ತರಬೇತಿ ಪಡೆಯಿನ ಬೊಕ್ಕ ಸಾಧನೆ ಮಲ್ತಿನ ಕೊಳಲು ವಾದಕೆರ್. ಎಚ್ ಎಸ್ ವೇಣುಗೋಪಾಲ್ ಕಳೆದ್ ಪನ್ನೆರಡು ವರ್ಸೊಡ್ದಿಂಚಿ ಅಲೆನ ಗುರು ಆದ್ ಉಲ್ಲೆರ್. ಏಳ್ ವರ್ಸೊದ ಪ್ರಾಯೊಡು ಬೆಂಗಳೂರು ಗಾಯನ ಸಮಾಜೊಡು ಅರೆನ ಸುರುತ ಫಾರ್ಮ್ಯಾಟ್ ಸಂಗೀತ ಕಛೇರಿನ್ ನಡಪಾಯೆರ್. <ref name=":0"/> <ref>{{Cite web|last=Vasanth|first=Kusumitha|date=2024-06-26|title=Varijashree Venugopal ties the threads of global music to weave 'Vari,' a refreshing album {{!}}|url=https://theindianmusicdiaries.com/varijashree-venugopal-ties-the-threads-of-global-music-to-weave-vari-a-refreshing-album/|accessdate=2024-11-15|website=theindianmusicdiaries.com|language=en-US|archivedate=2025-01-26|archive-url=https://web.archive.org/web/20250126120031/https://theindianmusicdiaries.com/varijashree-venugopal-ties-the-threads-of-global-music-to-weave-vari-a-refreshing-album/|url-status=dead}}</ref> <ref>{{Cite news|url=https://timesofindia.indiatimes.com/entertainment/kannada/music/music-was-the-first-language-i-knew-varijashree-venugopal/articleshow/107148540.cms|title=Music was the first language I knew: Varijashree Venugopal|date=2024-01-26|work=The Times of India|access-date=2024-11-15|issn=0971-8257}}</ref>ವಾರಿಜಶ್ರೀ ಸಂಗೀತಗಾರೆರ್ ಪ್ರವೀಣ್ ಡಿ ರಾವ್, ಅಜಯ್ ವಾರಿಯರ್ ಬೊಕ್ಕ ಪ್ರಮತ್ ಕಿರಣ್ ನಕುಲೆನೊಟ್ಟುಗು ಬೆಂಗಳೂರು ಬ್ಯಾಂಡ್ "ಚಕ್ರಫೋನಿಕ್ಸ್" ದ ಭಾಗವಾದ್ ಉಲ್ಲೆರ್. <ref>{{Cite web|title=Krautrock meets Indian classical music in Ragawerk's self-titled album — review|url=https://www.ft.com/content/c5ad2223-384a-4bc2-aeae-d5551bfc4518|accessdate=2024-04-27|website=www.ft.com|date=28 September 2022|last=Honigmann|first=David}}</ref> ಚಕ್ರಫೋನಿಕ್ಸ್ ಬೇತೆ ಸಂಗೀತ ಪ್ರಕಾರೊಲೆನೊಟ್ಟುಗು ಸಾಂಪ್ರದಾಯಿಕ ಶಾಸ್ತ್ರೀಯ ಸಂಗೀತೊದ ಅನನ್ಯ ಮಿಶ್ರಣೊಗು ಪುದರ್ ಪೋತುಂಡು. <ref>{{Cite news|url=https://timesofindia.indiatimes.com/entertainment/kannada/music/bengaluru-to-witness-a-virtual-concert-by-chakrafonics/articleshow/75649605.cms|title=Bengaluru to witness a virtual concert by Chakrafonics|date=2020-05-09|work=The Times of India|access-date=2024-04-27|issn=0971-8257}}</ref> ವಾರಿಜಶ್ರೀ ಸಂಗೀತಗಾರೆರ್ ಪ್ರವೀಣ್ ಡಿ ರಾವ್, ಅಜಯ್ ವಾರಿಯರ್ ಬೊಕ್ಕ ಪ್ರಮತ್ ಕಿರಣ್ ನಕುಲೆನೊಟ್ಟುಗು ಬೆಂಗಳೂರು ಬ್ಯಾಂಡ್ "ಚಕ್ರಫೋನಿಕ್ಸ್" ದ ಭಾಗವಾದ್ ಉಲ್ಲೆರ್. <ref>{{Cite web|title=Krautrock meets Indian classical music in Ragawerk's self-titled album — review|url=https://www.ft.com/content/c5ad2223-384a-4bc2-aeae-d5551bfc4518|accessdate=2024-04-27|website=www.ft.com|date=28 September 2022|last=Honigmann|first=David}}</ref> ಚಕ್ರಫೋನಿಕ್ಸ್ ಬೇತೆ ಸಂಗೀತ ಪ್ರಕಾರೊಲೆನೊಟ್ಟುಗು ಸಾಂಪ್ರದಾಯಿಕ ಶಾಸ್ತ್ರೀಯ ಸಂಗೀತೊದ ಅನನ್ಯ ಮಿಶ್ರಣೊಗು ಪುದರ್ ಪೋತುಂಡು. <ref>{{Cite news|url=https://timesofindia.indiatimes.com/entertainment/kannada/music/bengaluru-to-witness-a-virtual-concert-by-chakrafonics/articleshow/75649605.cms|title=Bengaluru to witness a virtual concert by Chakrafonics|date=2020-05-09|work=The Times of India|access-date=2024-04-27|issn=0971-8257}}</ref> ಕನ್ನಡ ಸೋಪ್ ಲೆಗ್ ಗಾಯನ ಬೊಕ್ಕ ಸಂಯೋಜನೆ ಮಲ್ಪುನವುಲಾ ಅರೆನ ಕೆಲಸೊಲೆಡ್ ಉಂಡು ಬೊಕ್ಕ ಹೆಲ್ವನಕಟ್ಟೆ ಗಿರಿಯಮ್ಮ ಬೊಕ್ಕ ಮಗಲು ಜಾನಕಿ ಗ್ ಅರೆನ ಸ್ವರೊನು ಕೊರ್ದು ಜನಪ್ರಿಯವಾದ್ ಪುದರ್ ಪಡೆಯೆರ್, ಉಂದೆಕ್ ಅರೆನ ವ್ಯಾಪಕ ಸಾರ್ವಜನಿಕ ಮೆಚ್ಚುಗೆನ್ ಪಡೆಯೆರ್. <ref>{{Cite news|url=https://tv9kannada.com/entertainment/sandalwood/varijashree-venugopal-who-sung-magalu-janaki-title-song-nominated-for-grammy-awards-rmd-939322.html|title=ಗ್ರ್ಯಾಮಿ ನಾಮಿನೇಷನ್ ನಲ್ಲಿ 'ಮಗಳು ಜಾನಕಿ' ಟೈಟಲ್ ಸಾಂಗ್ ಗಾಯಕಿ ವಾರಿಜಾಶ್ರೀ ವೇಣುಗೋಪಲ್ ಹೆಸರು|date=23 November 2024}}</ref> == ಸಂಗೀತ ತರಬೇತಿ == {| class="wikitable" style="margin:auto" |+ ! ಗುರು ! ಪ್ರಕಾರ ! ಕಾಲಾವಧಿ ! ವಿವರ |- | ಹೆಚ್ ಎಸ್ ವೇಣುಗೋಪಾಲ್ ಬೊಕ್ಕ ರಾಮ ವೇಣುಗೋಪಾಲ್ | ಕಾರ್ನಾಟಕ | ೧೯೯೨-ಇತ್ತೆ | ಎಲ್ಯ ಬಾಲೆ ಆದ್ ತನ್ನ ಸುರುತ ಪಾಠೊಲೆನ್ ಸುರು ಮಲ್ತೆರ್. |- | ವಿದುಶಿ ಹೆಚ್ ಗೀತಾ | ಕಾರ್ನಾಟಕ | ೧೯೯೮-೨೦೦೫ | ಕರ್ನಾಟಕ ಸಂಗೀತೊಡು ಅಡಿಪಾಯೊಲು. |- | ಶ್ರೀ. ವಿಡ್. ಸುಂದರೇಶನ್ | ಕಾರ್ನಾಟಕ | ೨೦೦೫-೨೦೧೧ | ಸಂಗೀತ ಬೊಕ್ಕ ತರಬೇತಿಡ್ ಮುನ್ನಡೆ. |- | ವಿಧುಶಿ ವಸಂತ ಶ್ರೀನಿವಾಸನ್ ಹಾಗೂ ವಿದ್ವಾನ್ ಡಿ ಎಸ್ ಶ್ರೀವತ್ಸ | ಕಾರ್ನಾಟಕ | ೨೦೦೮ | ಸಂತೊಲೆನ ಅಪರೂಪದ ಸಂಯೋಜನೆಲು. |} == ವಿಶ್ವ ಸಂಗೀತದ ಪ್ರಯೋಗೊಲು == 2018ಡ್, ವರಿಜಶ್ರೀ ಬಾಲ್ಯದ ಒಲವುಡ್ದ್ ವಿಶ್ವ ಸಂಗೀತ ಬೊಕ್ಕ ಜಾಝ್ ನ್ ಅನ್ವೇಷಣೆ ಮಲ್ಪೆರೆ ಸುರು ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಅರೆನ ಚಿಂತನೆದ ಪ್ರಕ್ರಿಯೆದ ಒಂಜಿ ಅನನ್ಯ ಮಿಶ್ರಣೊನು ಪೋಸ್ಟ್ ಮಲ್ಪೆರೆ ಸುರು ಮಲ್ತೆರ್, ಉಂದು ಜಾಝ್ ಗ್ ಕರ್ನಾಟಿಕ್ ಸಂಗೀತದ ಪದರೊಲೆನ್ ಸೇರ್ಪಾಂಡ್, ಅಯಿಕ್ ಆರ್ "ಕಾರ್ನಾಟಿಕ್ ಸ್ಕ್ಯಾಟ್" ಗಾಯನ ಪನ್ಪಿನ ಪದನ್ ರಚನೆ ಮಲ್ತೆರ್. <ref>{{Cite news|url=https://www.thehindu.com/entertainment/music/the-human-voice-is-an-instrument-artiste-varijashree-venugopal-on-the-release-of-her-debut-album-vari/article68175201.ece|title=The human voice is an instrument: Artiste Varijashree Venugopal on the release of her debut album Vari|last=Dhanaraj|first=Ruth|date=16 May 2024|work=The Hindu}}</ref> ಪ್ರೇಕ್ಷಕೆರೆಗ್ ಬೊಕ್ಕ ಕಲಾವಿದೆರೆಗ್ ಒಂಜೇ ರೀತಿಡ್ ತಲುಪುನಗ ಆರ್ ಆನ್ ಲೈನ್ ಮಾಧ್ಯಮೊನು ಬಳಕೆ ಮಲ್ತೆರ್. ಕ್ಷೇತ್ರೊಡು ಪೊಸ ಪ್ರಗತಿನ್ ಅರ್ಥ ಮಲ್ಪೆರೆ ಪ್ರಯತ್ನ ಮಲ್ಪುನಗ ಬಾಬಿ ಮೆಕ್ ಫೆರ್ರಿನ್ <ref>{{Cite news|url=https://www.thehindu.com/entertainment/music/the-human-voice-is-an-instrument-artiste-varijashree-venugopal-on-the-release-of-her-debut-album-vari/article68175201.ece|title=The human voice is an instrument: Artiste Varijashree Venugopal on the release of her debut album Vari|last=Dhanaraj|first=Ruth|date=16 May 2024|work=The Hindu}}</ref> ನಂಚಿನ ಜನಕುಲು ಅಲೆನ್ ತಲುಪುನವು ಅಲೆಗ್ ಆಶ್ಚರ್ಯ ಆಂಡ್. ಅಪಗನೇ ಅರೆನ ಸಹಯೋಗೊಲು EYM ಟ್ರೈಯೊ, ಸ್ನಾರ್ಕಿ ಪಪ್ಪಿ ಬೊಕ್ಕ ಹ್ಯಾಮಿಲ್ಟನ್ ಹೊಲ್ಲಾಂಡಾ ಇಂಚಿನ ಕೆಲವು ಸ್ವತಂತ್ರ ಕಲಾವಿದೆರೆನೊಟ್ಟುಗು ಸುರು ಆಂಡ್. == ಕಲಾರ್ನವ ಸಂಗೀತ ಉತ್ಸವ == ವರಿಜಾಶ್ರೀ ತನ್ನ ಅಪ್ಪೆ ಎಚ್ ಎಸ್ ವೇಣುಗೋಪಾಲ್ ಬೊಕ್ಕ ಅಮ್ಮೆನೊಟ್ಟುಗು ಬೆಂಗಳೂರು ಮೂಲದ ಕಲಾ ಉತ್ಸವ ಕಲಾರ್ನವ (ಅರ್ಥ ಕಲಾ ರೂಪೊಲೆನ ಅರಳುನ) ದ ನಿರ್ದೇಶಕೆರ್ ಆದ್ ಉಲ್ಲೆರ್, ಉಂದು ಜಗತ್ತ್ ದ ಮಾತಾ ರೀತಿದ ಕಲಾವಿದೆರೆಗ್ ಒಂಜಿ ಮಾರ್ಗ ಆತ್ಂಡ್. ಈ ಉತ್ಸವ 2005 ವರ್ಷಡ್ದ್ ನಡತೊಂದು ಬೈದ್ಂಡ್. <ref>{{Cite news|url=https://timesofindia.indiatimes.com/entertainment/events/bangalore/kalaarnava-music-and-art-festival-goes-online-this-year/articleshow/76220456.cms|title=Kalarnava News festival}}</ref> ಈ ಉತ್ಸವೊಡು ಹರಿಹರನ್ (ಗಾಯಕ) ಬೊಕ್ಕ [[ಎಸ್.ಪಿ.ಬಾಲಸುಬ್ರಹ್ಮಣ್ಯಂ|ಎಸ್ ಪಿ ಬಾಲಸುಬ್ರಹ್ಮಣ್ಯಂ]] ಸೇರ್ದ್ ದೇಶೊದ ಮಾತಾ ಕಲಾವಿದೆರ್ ಉತ್ಸವೊದ ಘಟನೆದ ಪ್ರಸ್ತುತಿ ಕಲಾವತಂಸೊದ ಪ್ರಶಸ್ತಿ ಪಡೆಯಿನಕುಲು. == ಪ್ರಶಸ್ತಿಲು == 2024ಡ್ ಜಾಕೋಬ್ ಕೊಲಿಯರ್ ನ "ಎ ರಾಕ್ ಸಮ್ವೇರ್ ಅತ್ಯುತ್ತಮ ಜಾಗತಿಕ ಸಂಗೀತ ಪ್ರದರ್ಶನ ವಿಭಾಗೊಡು ತನ್ನ ಸುರುತ ಗ್ರ್ಯಾಮಿ ನಾಮನಿರ್ದೇಶನೊನು <ref>{{Cite web|last=Gowda|first=Dese|date=2024-11-11|title='A pat on the back for independent artistes', says Varijashree Venugopal on her Grammy nomination|url=https://www.newindianexpress.com/cities/bengaluru/2024/Nov/11/a-pat-on-the-back-for-independent-artistes-says-varijashree-venugopal-on-her-grammy-nomination|accessdate=2024-11-15|website=The New Indian Express|language=en}}</ref>.ಭಾರತದ ರಾಜ್ಯ ಬೊಕ್ಕ ಕೇಂದ್ರ ಸರ್ಕಾರೊಲೆಡ್ದ್ ಮಾನ್ಯತೆ ಸೇರ್ದ್ ಕೆಲವು ಪ್ರಶಸ್ತಿಲೆನ್ ಲಾ ಆರ್ ಪಡೆಯೆರ್. == ಆಲ್ಬಮ್ ಲು == ವರಿಜಾಶ್ರೀ 'ಅರ್ಪಣ' ಬೊಕ್ಕ 'ಉಪಾಸನ' ಪನ್ಪಿನ ಸಂಗೀತ ಆಲ್ಬಂಲೆನ್ ಮಲ್ತೊಂದುಲ್ಲೆರ್, ಇತ್ತೆ ಬುಡುಗಡೆ ಆಯಿನ 'ಮೇಳ ರಾಗ ಮಾಲೀಕ', 'ಬಿದಿರು' ಬೊಕ್ಕ 'ಕಾಯೋ ಎನ್ನ ಗೋಪಾಲ' ಅರೆನ ಶ್ರೇಯೊಗು ಕಾರಣ ಆತೆರ್. ವಿದ್ ನಿರ್ದೇಶನದ 'ಅಷ್ಟಾವಧಾನ' ಮುಂತಾದ ಡಾಕ್ಯುಮೆಂಟರಿ ಸಿನಿಮಾಲೆಗ್ ಮೇರ್ ಸಂಗೀತ ಮಲ್ತೆರ್. ಶತಾವಧಾನಿ ಡಾ.ಆರ್.ಗಣೇಶ್, ಶ್ರೀ ಕಾರ್ತಿಕ್ ಮತ್ತು ಶ್ರೀ ವಿವೇಕ ಆರಗ ನಿರ್ದೇಶನದ 'ಚಿಗುರು' ಹಾಗೂ 'ಪುಟ್ಟ ನಿಧಿಗಳು' ಕಿರುಚಿತ್ರಗಳು. <ref>{{Cite web|url=http://www.newindianexpress.com/cities/bengaluru/article353019.ece|archiveurl=https://web.archive.org/web/20160304213626/http://www.newindianexpress.com/cities/bengaluru/article353019.ece|archivedate=4 March 2016|title=Soothing classical music}}</ref> Gಗ್ರ್ಯಾಮಿ ಪ್ರಶಸ್ತಿ ಪಡೆಯಿನ ಕಲಾವಿದೆ ಮೈಕೆಲ್ ಲೀಗ್ ನ ಸಹಕಾರೊಡು ಹ್ಯಾಮಿಲ್ಟನ್ ಹೊಲ್ಲಾಂಡಾ, ರಾಜೇಶ್ ವೈಧ್ಯ, ಪ್ರಮತ್ ಕಿರಣ್, ಪ್ರವೀಣ್ ಡಿ ರಾವ್ ಸೇರ್ದ್ ಒಂಜಿ ಸ್ವತಂತ್ರ ಆಲ್ಬಮ್ 'ವರಿ' ನ್ ಲಾ ರಚನೆ ಮಲ್ತೆರ್. ಈ ಆಲ್ಬಮ್ ಡ್ 7 ಟ್ರ್ಯಾಕ್ ಲು ಉಂಡು, ಒಂಜಿ ಟ್ರ್ಯಾಕ್ ಒಂಜಿ ಅನನ್ಯ ಆಯಾಮದ ರುಚಿನ್ ತೂಪುಂಡು, ಉಂದು ಭಾರತೀಯ ಚಿಂತನೆದ ರೀತಿನ್ ಬೊಕ್ಕ ಅಯಿತ ಸಾರ್ವತ್ರಿಕತೆನ್ ವ್ಯಕ್ತಪಡಿಸವುಂಡು, ಉಂದು ಬಹು ಕಲಾ ರೂಪೊಲೆನ ಸುಗಮ ಮಿಶ್ರಣೊನು ಸಕ್ರಿಯ ಮಲ್ಪುಂಡು. == ಬ್ಯಾಂಡ್ ಲೆನ್ ಪ್ರದರ್ಶನ ಮಲ್ಪುನ == {| class="wikitable" style="margin:auto" !ಬ್ಯಾಂಡ್ ಪುದರ್ ! ಭಾಷೆ ! ಸಹ ಕಲಾವಿದೆರ್ ! ಪ್ರಕಾರ |- | ಚಕ್ರಫೋನಿಕ್ಸ್ | ಬಹುಭಾಷಿಕ | ಪ್ರಮತ್ ಕಿರಣ್, ಪ್ರವೀಣ್ ಡಿ ರಾವ್, ಅಜಯ್ ವಾರಿಯರ್ | ಕರ್ನಾಟಿಕ್ ಫ್ಯೂಷನ್ |- | ಇವೈಎಂ ಟ್ರೈಯೊ | ಬಹುಭಾಷಿಕ | ಎಲಿ ಡುಫೋರ್, ಯಾನ್ ಫೈಫೆಟ್, ಮಾರ್ಕ್ ಮಿಚೆಲ್ | ಜಾಝ್ |- | ಮೈಕೆಲ್ ಲೀಗ್ ನ ಸಹಯೋಗ | ಬಹುಭಾಷಿಕ | ರಾಜೇಶ್ ವೈದ್ಯ, ಮೈಕಲ್ ಲೀಗ್, ಪ್ರಮತ್ ಕಿರಣ್ ಬೊಕ್ಕೊರ್ಮೆ | ಬಹು ಪ್ರಕಾರ |- | ಟ್ರೈಯೊ ಎಚ್ ಎಲ್ ಕೆ ನ ಸಹಯೋಗ | ಕರ್ನಾಟಕ ಸ್ಕ್ಯಾಟ್ | ರಿಚ್ ಹ್ಯಾರೊಲ್ಡ್ | ರಿಚ್ ಹ್ಯಾರೊಲ್ಡ್, ಆಂಟ್ ಲಾ, ರಿಚ್ ಕಾಸ್ |} {| class="wikitable" style="margin:auto" |+ವಾಣಿಜ್ಯ ಚಿತ್ರೊಲೆಡ್ ಮುಖ್ಯ ಪಾತ್ರೊಡು ಪಾಡ್ದನೊಲು. ! ಪಾಡ್ದನದ ಪುದರ್ . ! ಸಿನಿಮಾ ! ಭಾಷೆ ! ಸಹ ಕಲಾವಿದೆರ್ !<nowiki> ಸಂಗೀತ ನಿರ್ದೇಶನ |</nowiki> ! |- | ಡಾರ್ಲಿಂಗ್ ಡಾರ್ಲಿಂಗ್ | ಪ್ರೀತಿದ | ಕನ್ನಡ | ಅನೂಪ್ ಸೀಲಿನ್ | ಅನೂಪ್ ಸೀಲಿನ್ |- | ಮಧುರ ಮಧುರ | ಕಡಲ ತೀರದ ಭಾರ್ಗವೆ | ಕನ್ನಡ | ಅನಿಲ್ ಸಿ.ಜೆ. | ಅನಿಲ್ ಸಿ.ಜೆ. |- | ನೀನೆ ನನ್ನ ಅಲಪನೆ | ಕಡಲ ತೀರದ ಭಾರ್ಗವೆ | ಗನ್ನಡ | ಏಕವ್ಯಕ್ತಿ | ಅನಿಲ್ ಸಿ.ಜೆ. |- | ಯಾರೆ ನೀನು ಚತುರೆ | ತಗರು | ಗನ್ನಡ | ಏಕವ್ಯಕ್ತಿ | ಚರಣ್ ರಾಜ್ |- | ಪಾಯುಮ್ ಥೀಯೆ | ಪೊರ್ | ತಮಿಳು | ಸಂಜಿತ್ ಹೆಗ್ಡೆ | ಸಂಜಿತ್ ಹೆಗ್ಡೆ |- |<nowiki> ನಾ ಪ್ರಣಂ |</nowiki> | ಮೆಹಬೂಬಾ | ತೆಲುಗು | ಏಕವ್ಯಕ್ತಿ | ಸಂದೀಪ್ ಚೌಟ |- | ಸಪ್ತಸ್ವರ | ಆಚಾರ್ ಬೊಕ್ಕ ಕೋ | ಕನ್ನಡ | ಏಕವ್ಯಕ್ತಿ | ಬಿಂದುಮಾಲಿನಿ |} == ಉಲ್ಲೇಖೊಲು == {{Reflist}} [[ವರ್ಗೊ:Articles with short description]] [[ವರ್ಗೊ:ಪರಿಶೀಲಿಸಂದಿನ ಅನುವಾದೊಲು ಇಪ್ಪುನ ಪುಟೊಕುಲು]] 45njxvay2l325cty53l6f9z69d2yj7d ಕುಡು 0 26414 366095 359578 2026-08-21T00:43:43Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366095 wikitext text/x-wiki [[File:Horse Gram BNC.jpg|thumb|ಇಡಿ ಕುಡುತ್ತ ಬೇಳೆ]] '''ಕುಡು''' (ಸಸ್ಯಶಾಸ್ತ್ರೀಯ ಪುದರ್- ''ಮ್ಯಾಕ್ರೊಟೈಲೋಮಾ ಯುನಿಫ್ಲೋರಮ್'') ಉಷ್ಣವಲಯೊದ ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಮೂಲದ ಒಂಜಿ ದ್ವಿದಳ ಧಾನ್ಯ. ಇಂದೆತ ವಿಶಿಷ್ಟ ರುಚಿ ಬೊಕ್ಕ ರಚನೆ, ಇಂದೆಟ್ ಇಪ್ಪುನ ಪೋಷಕಾಂಶೊಲು ಬೊಕ್ಕ ಔಷಧೀಯ ಗುಣಲಕ್ಷಣೊಡ್ದಾವರ ದಿಂಜ ಗಲಸುವೆರ್. ಕುಡು ಅಂಗಡಿಡ್ ಕಡಮೆ ಬಿಲೆಕ್ಕ್ ತಿಕ್ಕುಂಡು ಅತ್ತಂದೆ ಕುದುರೆಗ್ ಎಚ್ಚ ತಾಕತ್ ಬುಕ್ಕ ಬಲ ಕೊರ್ಪುನೆಡ್ದಾವರ, ಕುಡುನು ಬಲಿಪುನ ಕುದುರೆಲು(ಕುದುರೆ ಓಟ) ಬುಕ್ಕ ಬೇಲೆ ಮಲ್ಪುನ ಕುದುರೆಲೆಗ್ ಜಾಸ್ತಿ ಕೊರ್ಪೆರ್. ಅಂಚಾದ್ ಕುಡುಕ್ಕು ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಹಾರ್ಸ್‌ಗ್ರಾಮ್ (Horsegramm) ಇಂದ್ ಪುದರ್ ಉಂಡು. ಭಾರತ, ನೇಪಾಳ, ಮಲೇಷ್ಯಾ, ಶ್ರೀಲಂಕಾ, ಬೊಕ್ಕ ವೆಸ್ಟ್ ಇಂಡೀಸ್ ದ ಕೆಲವು ಭಾಗೊಲೆಡ್ ಇಂದೆನ್ ಬುಳೆಪಾವೆರ್. ಕುಡುನು ಇಡಿಯಾದ್, ಮುಂಗೆ ಬರ್ಪಾದ್, ಇಜ್ಜಿಡ, ಪೊಡಿ ಮಲ್ತ್‌ದ್ಲಾ ತಿನೊಲಿ. ಸೀಕ್ ಸಂಕಡ ಮಿನಿ ಬನ್ನಗ ಇಲ್ಲದ ಮರ್ದ್‌ಗ್ಲಾ ಕುಡುನು ಗಲಸುವೆರ್. ==ಇವರೊಲು== ಕುಡು ವರ್ಸೊಗು ಒರ ಬುಲೆಪಿನ, ಬೂರುದಲೆಕ ಬುಳೆವುನ ದಯಿ. ಕುಡುತ್ತ ಬೀಜೊಲೆನ್ ಮಣ್ಣ್‌ಡ್ ಪಾಡಿಂಡ, ಮುಂಗೆ ಬತ್ ಬುಳೆವುಂಡು. ಉಂದು ಸುಮಾರು 60 ಸೆಂ.ಮೀ ಎತ್ತರೊಗು ಬುಲೆಪುಂಡು. ದೈತ ಮೈದಂಡ್ ಪೂರ ಬೊಲ್ದು ಬಣ್ಣದ ಎಲ್ಯ ನೆಯಿ ಕೂಜೆಲ್‌ದ ಲೆಕ್ಕ ಇಪ್ಪುವ. ಮೂಜಿ ಇರೆಕುಲು ತೂವರ ಬಿಲ್ವಪತ್ರೆದ ಲೆಕ ಇಪ್ಪುನ ಗೆಲ್ಲ್‌ಲೆನ್ ಹೊಂದಿಪ್ಪುಂಡು. ಇರೆತ್ತ ತೊಟ್ಟು ಸುಮಾರು 7 ಸೆಂ.ಮೀ ಉದ್ದವಾದ್ ತೆತ್ತಿದಲೆಕ ಇಪ್ಪುಂಡು. ಪೂಕುಲು ಇರೆತ್ತ ಸಂದಿಡ್ ರಡ್ಡ್ ಅತ್ತಂದೆ ಮೂಜಿ ಪೂಕುಲು ಅರಲುವ. ಅತ್ತಂದೆ ಮಂಜೊಲು ಬಣ್ಣೊಡು ಇಪ್ಪುಂಡು. ಪೂತ ಉಲಾಯಿ ನೆರಳೆ ಬಣ್ಣದ ಬಿಂದಿ ಇಪ್ಪುಂಡು. ಪೂ ಆಯಿ ಬೊಕ್ಕ ಉದ್ದದ ಬೀಜದ ಕೋಡು ಆಪುಂಡು. ಉಂದು ಸುಮಾರು 8 ಸೆಂ.ಮೀ ಉದ್ದ ಇತ್ತ್‌ದ್, ಐತ ಉಲಾಯಿ ಕೆಂಪು-ಕಂದು ಅತ್ತಂದೆ ಕಪ್ಪು ಬಣ್ಣದ ಕುಡುತ್ತ ಬಿತ್ತ್‌ಲು ಇಪ್ಪುವ. ಈ ಬಿತ್ತ್‌ಲು ಸುಮಾರು 3 ಡ್ದ್ 6 ಮಿ.ಮೀ ಉದ್ದ ಇಪ್ಪುಂಡು. <gallery mode="packed"> File:Macrotyloma Uniflorum young plant 01.jpg|ಮರಾಯಿಡ್ ಕುಡುತ್ತ ದೈ File:Macrotyloma Uniflorum flower 01.jpg|ಪೂ File:Macrotyloma Uniflorum fruit 01.jpg|ಪಜ್ಜಿ ಕೋಡು </gallery> == ವಿತರಣೆ== ಕುಡು ಅಸಲ್‌ಗ್ ಉಷ್ಣವಲಯದ ದಕ್ಷಿಣ ಏಷ್ಯಾ ದೇಸೊಲೆ ಮೂಲೊದ ದೈ. ನಮ ದೇಸೊಡು ಪುರಾತನ ಕಾಲೊಡು ಪನ್ನಗ, ಕ್ರಿಸ್ತ ಪೂರ್ವ 2500 ವರ್ಷೊಡ್ದು ಪಿರಾಕ್‌ಡೆ ಬುಲೆವೊಂದು ಇತ್ತೆರ್ಂದ್ ತೆರಿದ್ ಬೈದ್ಂಡ್. ಉಂದು ಭಾರತೊಡ್ದು ಮ್ಯಾನ್ಮಾರ್ ಮುಟ್ಟ ಒಂಜಿ ಪ್ರಮುಖವಾಯಿನ ಬೇಳೆಕಾಳು ಆದ್ ಬುಲೆಪಾವೆರ್. ಆಗ್ನೇಯ ಏಷ್ಯಾದ ದೇಸೊಲೆಡ್ ಬುಕ್ಕ ಉತ್ತರ ಆಸ್ಟ್ರೇಲಿಯಾದಂಚಿನ ಉಷ್ಣವಲಯದ ಬಾಗೊಲೆಡ್ ಪಶು ಆಹಾರವಾದ್ ಬುಲೆಪಾವೆರ್ ಅತ್ತಂದೆ, ಪಜಿ ಗೊಬ್ಬರದ ರೂಪೊಡುಲಾ ಗಲಸುವೆರ್. == ಬುಲೆಚಿಲ್== ಕುಡುನ್ ಮುಖ್ಯ ಬುಲೆಯಾದ್ ಅತ್ತಂದೆ ರಡ್ಡನೇ ಬುಲೆಯಾದ್ ಬುಲೆಪಾವೆರ್. ಕುಡು ವಾ ರೀತಿದ ಹವಮಾನೊ ಬುಕ್ಕ ಮಣ್ಣ್‌ದೊಟ್ಟುಗು ಸುಲಬೊಡು ಬುಲೆಪುನ ಒಂಜಿ ಬೇಳೆ. ಸರಾಸರಿ ಒಂಜಿ ಹೆಕ್ಟೇರ್‌ಗ್ 460 ಕಿ.ಗ್ರಾಂ ಇಳುವರಿ ಕುಡುಟ್ಟು ಬರ್ಪುಂಡು. ಸುರುಟು ಪಂಡಿಲೆಕ ಕುಡು ಒವ್ವೇ ರೀತಿದ ಮಣ್ಣ್‌ಡ್ ಬುಲೆಪುಂಡು. ಬೀಜ ಪಾಡನಗ ಕೊಟ್ಯದ ಗೊಬ್ಬರ ಪಾಡ್ಂಡ ಎಚ್ಚ ಇಳುವರಿ ಪಡೆಯೊಲಿ. ಮರಿಯಾಲದ ಸುರುಟು ಬೀಜ ಪಾಡುವೆರ್ ಇಜ್ಜಿಂಡ ಮದ್ಯ ಮರಿಯಾಲೊಡುಲಾ ಬೀಜ ಪಾಡೊಲಿ. ಕುಡು ೫-೬ ತಿಂಗೊಲ್ದ ಬುಳೆ. ವರ ಬಿತ್ ಪಾಡಿಬೊಕ್ಕ ೬ ತಿಂಗೊಲ್ದ ಉಲಾಯಿ ಕುಡುತ್ತ ಕೋಡು ಕೊಯ್ಯರ ತಿಕ್ಕುಂಡು.<ref>{{cite web |title=ರೈತರು ಹುರುಳಿ ಬೆಳೆ ಬೆಳೆದು ಉತ್ತಮ ಇಳುವರಿ ಪಡೆಯಿರಿ |url=https://kannada.krishijagran.com/agripedia/grow-horse-gram-and-get-better-yield/ |website=kannada.krishijagran.com |publisher=Krishi Jagran Media Group |access-date=23 April 2026 |archivedate=30 April 2026 |archive-url=https://web.archive.org/web/20260430130658/https://kannada.krishijagran.com/agripedia/grow-horse-gram-and-get-better-yield/ |url-status=dead }}</ref> ==ಪೋಷಕಾಂಶೊಲು== ಕುಡು ಉಷ್ಣವಲಯ ಬೊಕ್ಕ ಉಪೋಷ್ಣವಲಯದ ದ್ವಿದಳ ದಾನ್ಯೊ. ಉಂದು ಹೆಚ್ಚಾದ್ ನುಂಗೆಲ್ ಭೂಮಿಡ್ ಬುಲೆಪಾವೆರ್. ಬೇತೆ ದ್ವಿದಳ ಧಾನ್ಯೊಲೆನಲೆಕ್ಕನೆ ಇಂದೆಟ್ 'ಮೆಥಿಯೋನೈನ್' (methionine) ಬೊಕ್ಕ 'ಟ್ರಿಪ್ಟೊಫಾನ್' (tryptophan) ಕಮ್ಮಿ ಉಂಡು, ಆಂಡ ಕರ್ಬದಂಶ (iron) ಬೊಕ್ಕ 'ಮಾಲಿಬ್ಡಿನಮ್' (molybdenum) ಮಸ್ತ್ ಉಂಡು. ಬೇತೆ ಧಾನ್ಯೊಲೆಗ್ ಹೋಲಿಕೆ ಮಲ್ತ್ಂಡ, ಕುಡುತ ಬಿತ್ತ್‌ಲೆಡ್ 'ಟ್ರಿಪ್ಸಿನ್ ಇನ್ಹಿಬಿಟರ್', 'ಹೆಮಗ್ಗ್ಲುಟಿನಿನ್' ಬೊಕ್ಕ ನೈಸರ್ಗಿಕ 'ಫೀನಾಲ್' ಅಂಸೊಲು ಹೆಚ್ಚ ಉಂಡು. ಇಂದೆಟ್ ಉಪ್ಪುನ ನೈಸರ್ಗಿಕ ಫೀನಾಲ್‌ಲು ಮುಖ್ಯವಾದ್ ಫೀನಾಲಿಕ್ ಆಮ್ಲೊಲು. ೧೦೦ ಗ್ರಾಂ ನುಂಗೆಲ್ ಕುಡುಟು ಕಾರ್ಬೋಹೈಡ್ರೇಟ್ (57.2%), ಪ್ರೋಟೀನ್ (22%), ನಾರ್ (5.3%), ಕೊಬ್ಬು (0.50%), ಕ್ಯಾಲ್ಸಿಯಂ (287 mg), ಫಾಸ್ಫರಸ್ (311 mg), ಕರ್ಬದಂಶ (6.77 mg) ಬೊಕ್ಕ 321 ಕಿಲೋ ಕ್ಯಾಲರಿ ಶಕ್ತಿ ಉಂಡು. ಅಂಚನೆ ಥಯಾಮಿನ್, ರಿಬೋಫ್ಲಾವಿನ್ ಬೊಕ್ಕ ನಿಯಾಸಿನ್ ಜೀವಸತ್ವೊಲುಲಾ ಉಂಡು. ಇಂದೆತ ಪೌಷ್ಟಿಕಾಂಶ ಮಣ್ಣ್‌ದ ಗುಣ ಬೊಕ್ಕ ಹವಾಮಾನದ ಮಿತ್ತ್ ಕೂಡ ಅವಲಂಬಿತ ಆದಿಪ್ಪುಂಡು. ಉಂದು ಎಚ್ಚ ರುಚಿ ಇಜ್ಜನೆಡ್ದಾವರ ಜನ ಉಂದೆನ್ ತೆನಸ್‌ಗ್ ಎಚ್ಚ ಗಲಸುಜೆರ್. ==ತೆನಸ್‌ಡ್ ಕುಡು== == ಮರ್ದ್‌ದ ರೂಪೊಡು== ==ಎಚ್ಚ ಓದುಗು== * [[ಕುಡುತ್ತ ಚಟ್ನಿ]] * [[ಕುಡುತ್ತ ಸಾರ್]] ==ಉಲ್ಲೇಕ== {{reflist}} 15hdqfwop9xv8x8kaa9s8ox4rcj11je ಚಂಡೀಗಢ 0 27683 366145 363849 2026-08-21T07:23:26Z Mahaveer Indra 1023 366145 wikitext text/x-wiki '''ಚಂಡೀಗಢ''' ವಾಯುವ್ಯ ಭಾರತದ ಒಂಜಿ ನಗರ ಬೊಕ್ಕ [[ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಲು|ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶ]]. ಉಂದು [[ಪಂಜಾಬ್]] ಬೊಕ್ಕ [[ಹರ್ಯಾಣ]]- ರಡ್ಡ್ ರಾಜ್ಯೊಲೆಗ್ ರಾಜಧಾನಿ ಆದ್ ಉಂಡು. ಹಿಮಾಲಯದ ಶಿವಾಲಿಕ್ ಬೆಟ್ಟೊಲೆದ ಮುದೆಲ್‌ಡ್ ಉಂಡು. ಚಂಡೀಗಢತ್ತ ಮೂಡಾಯಿಡ್ ಹರಿಯಾಣ ರಾಜ್ಯ ಉಂಡು. ಒರಿದಿನ ಮೂಜಿ ದಿಕ್ಕ್‌ಡ್ ಪಂಜಾಬ್ ರಾಜ್ಯೊದ ಗಡಿ ಉಂಡು. ಉಂದು ನವದೆಹಲಿದ ವಾಯುವ್ಯೊಗು 260 ಕಿ.ಮೀ , ಅಮೃತಸರದ ಆಗ್ನೇಯೊಗು 229 ಕಿ.ಮೀ, ಬೊಕ್ಕ ಶಿಮ್ಲಾದ ನೈರುತ್ಯೊಗು 104 ಕಿ.ಮೀ ದೂರೊಡು ಉಂಡು. ಸ್ವಾತಂತ್ರ್ಯ ಬತ್ತಿಬೊಕ್ಕ ಭಾರತೊಡು ಸುರುಕು ಯೋಜನೆ ಮಲ್ತ್ ಕಟ್ಟಿನ ನಗರೊಲೆಡ್ ಚಂಡೀಗಢ ಒಂಜಿ. ನೆತ್ತ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ನಗರ ವಿನ್ಯಾಸೊಗು ಅಂತರರಾಷ್ಟ್ರೀಯ ಮಟ್ಟೊಡು ಪುಗರ್ತೆ ಪಡೆದ್ಂಡ್. ಈ ನಗರದ ಮಾಸ್ಟರ್ ಪ್ಲಾನ್‌ನ್ ಲೆ ಕಾರ್ಬೂಸಿಯರ್ ಪನ್ಪಿನ ವಾಸ್ತುಶಿಲ್ಪಿ ತಯಾರ್ ಮಲ್ತೆರ್. ಮುಲ್ತ ಎಚ್ಚದ ಸರ್ಕಾರಿ ಕಟ್ಟೊಣೊಲೆನ್ ಬೊಕ್ಕ ಇಲ್ಲುಲೆನ್ ಲೆ ಕಾರ್ಬೂಸಿಯರ್ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಿಲಾಯಿನ ಜೇನ್ ಡ್ರೂ, ಮ್ಯಾಕ್ಸ್‌ವೆಲ್ ಫ್ರೈ ಮೊಕುಲ್ನ ಗುಂಪು ವಿನ್ಯಾಸ ಮಲ್ತ್ಂಡ್. ಲೆ ಕಾರ್ಬೂಸಿಯರ್ ನ ಜಾಗತಿಕ ಕಟ್ಟೊಣೊಲೆದ ಒಂಜಿ ಭಾಗವಾಯಿನ ಚಂಡೀಗಢದ ಕ್ಯಾಪಿಟಲ್ ಕಾಂಪ್ಲೆಕ್ಸ್‌ನ್, ಜುಲೈ 2016 ಡ್ ನಡತಿನ 40 ನೇ ವಿಶ್ವ ಪರಂಪರೆ ಸಮ್ಮೇಳನೊಡು ಯುನೆಸ್ಕೋ 'ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ' ಪಂದ್ ಘೋಷಣೆ ಮಲ್ತ್ಂಡ್.<ref name="ci2">{{cite web|url=http://business.gov.in/investment_incentives/chandigarh.php|title=Business Portal of India : Investment Opportunities and Incentives : State Level Investment : Chandigarh|work=business.gov.in|access-date=12 March 2015|url-status=dead|archive-url=https://web.archive.org/web/20131017092219/http://business.gov.in/investment_incentives/chandigarh.php|archive-date=17 October 2013}}</ref> ==ಪುದರ್== ಚಂಡೀಗಢ ಪನ್ಪಿ ಪುದರ್‌ದ ಅರ್ಥ "ಚಂಡಿನ ಕೋಟೆ" ಪಂದ್. ಇಂದೆಟ್ ಚಂಡಿ ಬೊಕ್ಕ ಗಢ ಪನ್ಪಿ ರಡ್ಡ್ ಪದೊಕುಲು ಉಲ್ಲ. ಚಂಡಿ ಪಂಡ ಹಿಂದೂ ದೇವತೆ ಬೊಕ್ಕ ಗಢ ಪಂಡ ಕೋಟೆ ಅತ್ತಂಡ ಭದ್ರವಾಯಿನ ಜಾಗೆ ಪನ್ಪಿ ಅರ್ಥ ಉಂಡು. ಚಂಡಿ ದೇವಸ್ಥಾನ ಇತ್ತಿನ ಒಂಜಿ ಹಳ್ಳಿದ ಪುದರ್‌ಡ್ದ್ ಈ ಪುದರ್ನ್ ದೆತೊನ್‌ದೆರ್. ಈ ದೇವಸ್ಥಾನ ಇತ್ತೆಲಾ ನಗರದ ಪಿದಾಯಿಡ್, ಕೈತಲ್ದ ಹರಿಯಾಣದ ಪಂಚಕುಲ ಜಿಲ್ಲೆದ ಜಿಲ್ಲಾ ಕೇಂದ್ರೊಡು ಉಂಡು. ಇಡೆಗ್ ಚಂಡೀಗಢಡ್ದ್ ೧೫ ಕಿಲೋ ಮೀಟರ್ ದೂರ ಆಪುಂಡು.<ref>{{cite web|url=http://www.newkerala.com/states-of-india/chandigarh.php|title=Chandigarh: Info on geography, history, government, districts, business, economy, travel, rivers, education, food, arts, culture, music, dance, festivals|work=newkerala.com|access-date=12 March 2015|archive-url=https://web.archive.org/web/20150317182551/http://www.newkerala.com/states-of-india/chandigarh.php|archive-date=17 March 2015|url-status=live}}</ref> ==ಹವಾಮಾನ== ಚಂಡೀಗಢೊಡು ತೇವಾಂಶ ಉಪ್ಪುನ ಉಪೋಷ್ಣವಲಯದ ಹವಾಮಾನ ಉಂಡು. ಬಾರಿ ಬೆಚ್ಚದ ಅರೆಗಾಲ, ಹದ ಚಳಿ ಇಪ್ಪುನ ಚಳಿಗಾಲ, ನಂಬರೆ ಆವಂದಿನ ಬರ್ಸ, ಬೊಕ್ಕ ಮಲ್ಲ ಮಟ್ಟ್‌ದ ಉಷ್ಣಾಂಶದ ವ್ಯತ್ಯಾಸೊಲೆನ್ (−1 ಡ್‍ದ್ 45 °C ಅತ್ತಂಡ 30.2 ಡ್‍ದ್ 113.0 °F) ತೂವೊಲಿ. ಮುಲ್ತ ಸರಾಸರಿ ವಾರ್ಷಿಕ ಬರ್ಸ 1,038.4 ಮಿಲಿಮೀಟರ್ ಬರ್ಪುಂಡು. ಮೆಡಿಟರೇನಿಯನ್ ಸಮುದ್ರೊಡು ಪುಟ್ಟುನ ಪಶ್ಚಿಮದ ಅಡಚಣೆಲೆಡ್‍ದಾತ್ರ ಮುಲ್ಪ ಚಳಿಗಾಲೊಡುಲಾ ವರ ಬರ್ಸ ಬರ್ಪುಂಡು. ಈ ಪಶ್ಚಿಮದ ಅಡಚಣೆಲು ಎಚ್ಚಾದ್ ಡಿಸೆಂಬರ್ ನಡುಡ್‍ದ್ ಏಪ್ರಿಲ್ ಕಡೆ ಮುಟ್ಟ ಬರ್ಸನ್ ಕನಪುಂಡು. ಹವಾಮಾನ ಎಚ್ಚ ಚಳಿ ಆಂಡ (ಮಾರ್ಚ್-ಏಪ್ರಿಲ್ ತಿಂಗೊಲುಲೆಡ್), ಈ ಬರ್ಸದೊಟ್ಟುಗು ಜೋರ್ ಗಾಳಿ ಬೊಕ್ಕ ಆನೆಕಲ್ಲ್ ಬೂರುನಲಾ ಉಂಡು, ಉಂದು ಸಾಮಾನ್ಯವಾದ್ ಮುಲ್ತ ಬುಲೆಕ್ಲೆಗ್ ಬಾರಿ ನಷ್ಟ ಮಲ್ಪುಂಡು. ಚಳಿಗಾಲೊಡು ಹಿಮ ಬೂರುನ ಬಡಕಾಯಿಡ್ ಉಪ್ಪುನ ಹಿಮಾಲಯಡ್ದ್, ಚಳಿದ ವಾಯುವ್ಯ ಗಾಳಿ ಸಾಮಾನ್ಯವಾದ್ ಮುಲ್ತಡೆ ಬೀಸುಂಡು.<ref>{{cite web |title=Climate and Average Weather Year Round in Chandigarh |url=https://weatherspark.com/y/108778/Average-Weather-in-Chandigarh-India-Year-Round |website=weatherspark.com |publisher=WeatherSpark |access-date=3 August 2026}}</ref> ==ಜನಸಂಖ್ಯಾ ಶಾಸ್ತ್ರ== === ಜನಸಂಖ್ಯೆ=== 2011 ದ ಭಾರತದ ಜನಗಣತಿದ ಪ್ರಕಾರ, ಚಂಡೀಗಢೊಡು 1,055,450 ಜನಸಂಖ್ಯೆ ಉಂಡು. ಪ್ರತಿ ಚದರ ಕಿಲೋಮೀಟರ್‌ಗ್ ಸುಮಾರ್ 9,252 (2001 ಡ್ 7,900 ಇತ್ತ್ಂಡ್) ಜನ ಉಲ್ಲೆರ್.<ref>{{cite web|url=http://censusindia.gov.in/|title=Census of India Website : Office of the Registrar General & Census Commissioner, India|work=censusindia.gov.in|access-date=12 March 2015|url-status=dead|archive-url=https://web.archive.org/web/20080511035603/http://censusindia.gov.in/|archive-date=11 May 2008}}</ref> ಜನಸಂಖ್ಯೆಡ್ ಆಂಜೊವುಲು 55% ಬೊಕ್ಕ ಪೊಂಜೊವುಲು 45% ಉಲ್ಲೆರ್. ಲಿಂಗ ಅನುಪಾತದ ಪ್ರಕಾರ ಪ್ರತಿ 1,000 ಆಂಜೊವುಲು 818 ಪೊಂಜೊವುಲು ಉಲ್ಲೆರ್. ಜೋಕುಲೆನ ಲಿಂಗ ಅನುಪಾತ ಪ್ರತಿ ೧೦೦೦ ಆಂಜೊವುಲೆಗ್ 880 ಪೊಂಜೊವುಲು ಉಲ್ಲೆರ್. ಚಂಡೀಗಢದ ಸಾಕ್ಷರತಾ ಪ್ರಮಾಣ 86.77% (7 ವರ್ಷ ಬೊಕ್ಕ ಅವೆಡ್ದ್ ಮೇಲ್‌ದ ಪ್ರಾಯದ ಜನಸಂಖ್ಯೆದ ಆಧಾರದ ಮಿತ್ತ್) ಉಂಡು, ಉಂದು ರಾಷ್ಟ್ರೀಯ ಸರಾಸರಿಡ್ದ್ ಎಚ್ಚ ಉಂಡು; ನೆಟ್ಟ್ ಆಂಜೊವುಲೆದ ಸಾಕ್ಷರತೆ 90.81% ಬೊಕ್ಕ ಪೊಂಜೊವುಲೆದ ಸಾಕ್ಷರತೆ 81.88% ಉಂಡು. ಜನಸಂಖ್ಯೆಡ್ 10.8% ಜನಕುಲು 6 ವರ್ಷಡ್ದ್ ಎಲ್ಯ ಪ್ರಾಯದಕುಲು. 2011 ಡ್ ಚಂಡೀಗಢದ ಜನಸಂಖ್ಯೆ ಭಾರತದ ಒಟ್ಟು ಜನಸಂಖ್ಯೆದ 0.09 ಶೇಕಡಾದಾತ್ ಉಂಡು.<ref name="chandigarh.official">{{cite web|title=Statistical Abstract of Chandigarh|url=http://chandigarh.gov.in/stat18/Abstract2018/stat18-T2.pdf|website=Official Website of Chandigarh|access-date=8 June 2020}}</ref> ===ಬಾಸೆ=== ಚಂಡೀಗಢ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶದ ಆಡಳಿತೊಡು ಇಂಗ್ಲಿಷ್ ಬೊಕ್ಕ ಹಿಂದಿನ್ ಅಧಿಕೃತ ಭಾಷೆಲಾದ್ ಗಲಸುವೆರ್. ಮುಲ್ತ ಎಚ್ಚದ ಜನಕುಲು (67.76%) ಹಿಂದಿ ಪಾತೆರುವೆರ್, ಅಂಚನೆ 22.02% ಜನಕುಲು ಪಂಜಾಬಿ ಪಾತೆರುವೆರ್. ಸರ್ಕಾರಿ ಶಾಲೆಲೆಡ್ ಇಂಗ್ಲಿಷ್, ಹಿಂದಿ ಬೊಕ್ಕ ಪಂಜಾಬಿ ಪಠ್ಯಪುಸ್ತಕಲೆನ್ ಗಲಸುವೆರ್. ಪಂಜಾಬಿ ಪಾತೆರುನಕಲ್ನ ಶೇಕಡಾವಾರು 1981 ಡ್ 36% ಇತ್ತಿನವು 2011 ಗ್ 22% ಗ್ ಕಮ್ಮಿ ಆತ್ಂಡ್, ಆಂಡ ಹಿಂದಿ ಪಾತೆರುನಕಲ್ನ ಸಂಖ್ಯೆ 51% ಡ್‍ದ್ 67% ಗ್ ಎಚ್ಚ ಆತ್ಂಡ್.<ref>{{cite web|url=https://www.tribuneindia.com/news/chandigarh/after-mother-tongue-city-more-proficient-in-english/662470.html|title=After mother tongue, city more proficient in English|work=Tribuneindia News Service |date=3 October 2018|access-date=4 October 2018|archive-url=https://web.archive.org/web/20181004103927/https://www.tribuneindia.com/news/chandigarh/after-mother-tongue-city-more-proficient-in-english/662470.html|archive-date=4 October 2018|url-status=live |last1=Service |first1=Tribune News }}</ref> === ದರ್ಮೊ=== ಚಂಡೀಗಢೊಡು ಹಿಂದೂ ಧರ್ಮದಕ್ಲು ಎಚ್ಚ ಉಲ್ಲೆರ್. ಒಟ್ಟು ಜನಸಂಖ್ಯೆದ 80.71% ಜನಕುಲು ಹಿಂದೂದರ್ಮೊದಕ್ಲು ಉಲ್ಲೆರ್. ಸಿಖ್ ಧರ್ಮದಕ್ಲು 13.11% ಜನಕುಲು ಉಲ್ಲೆರ್. 4.87% ಜನಕುಲು ಇಸ್ಲಾಂ ಧರ್ಮೊದಕ್ಲು ಮುಲ್ಪ ಉಲ್ಲೆರ್. ಕ್ರಿಸ್ತನ್‌ದಕ್ಲು 0.83%, ಜೈನೆರ್ 0.19%, ಬೌದ್ಧೆರ್ 0.11% ಉಲ್ಲೆರ್, ವಾ ಧರ್ಮೊನುಲಾ ಪನಂದಿನಕುಲು 0.10%, ಬೊಕ್ಕ ಬಾಕಿದಕುಲು 0.02% ಉಲ್ಲೆರ್. == ಉಲ್ಲೇಕ== {{reflist}} [[ವರ್ಗೊ:ಭಾರತೊದ ರಾಜ್ಯ ರಾಜಧಾನಿಲು]] [[ವರ್ಗೊ:ತುಳು ವಿಕಿಪೀಡಿಯ 10ನೇ ವರ್ಸಾಂತ್ಯೊ]] fr3xs5y8va4izl7krq9ddr5u5v5aoz4 ರಾಂಚಿ 0 27694 366166 364150 2026-08-21T09:36:40Z Mahaveer Indra 1023 366166 wikitext text/x-wiki {{under construction}} '''ರಾಂಚಿ''' ನಮ ದೇಸೊದ [[ಝಾರ್ಖಂಡ್|ಜಾರ್ಖಂಡ್]] ರಾಜ್ಯದ ರಾಜಧಾನಿ ಬೊಕ್ಕ ಜನಸಂಖ್ಯೆದ ಆಧಾರೊಡು ಮಾಮಲ್ಲ ಜಿಲ್ಲೆ ಆದುಂಡು. ರಾಂಚಿ, ಜಾರ್ಖಂಡ್ ಚಳುವಳಿದ ಕೇಂದ್ರಲಾ ಆದಿತ್ಂಡ್. ಪನ್ನಗ ದಕ್ಷಿಣ ಬಿಹಾರ, ಉತ್ತರ ಒಡಿಶಾ, ಪಶ್ಚಿಮ ಬಂಗಾಲದ ಪಶ್ಚಿಮ ಬಾಗೊ ಬೊಕ್ಕ ಇತ್ತೆದ ಛತ್ತೀಸ್‌ಗಢದ ಮೂಡಾಯಿ ಬಾಗೊಲೆನ್ ಸೇರಾದ್ ಬುಡಕಟ್ಟು ಪ್ರದೇಶೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ರಾಜ್ಯೊ ಬೋಡು ಪನ್ಪಿನ ಬೇಡಿಕೆ ಈ ಚಳುವಳಿಡ್ ಇತ್ತ್ಂಡ್.<ref>{{Cite web | url=http://www.country-studies.com/india/jharkhand-movement.html | archive-url=https://web.archive.org/web/20050318181629/http://www.country-studies.com/india/jharkhand-movement.html | url-status=dead | archive-date=2005-03-18 | title=Jharkhand Movement | publisher= Country Studies | access-date=2009-05-07}}</ref> ಐಡ್ದ್ ಬುಕ್ಕ ೧೫ನೇ ನವೆಂಬರ್ ೨೦೦೦ನೇ ಇಸವಿಡ್ ಬಿಹಾರದ ಛೋಟಾ ನಾಗ್ಪುರ ಬೊಕ್ಕ ಸಂತಾಲ್ ಪರಗಣ ವಿಭಾಗೊಲೆನ್ ಬೇತೆ ಮಲ್ತ್‌ದ್ ಜಾರ್ಖಂಡ್ ರಾಜ್ಯೊನು ಉಂಡು ಮಲ್ತೆರ್. ಪ್ರಧಾನಿ ನರೇಂದ್ರ ಮೋದಿ ಸರ್ಕಾರದ ಸ್ಮಾರ್ಟ್ ಸಿಟೀಸ್ ಮಿಷನ್‌ದ ಅಡಿಟ್ ಅಭಿವೃದ್ಧಿ ಮಲ್ಪೆರೆ ಆಯ್ಕೆ ಆಯಿನ ನೂರು ನಗರೊಲೆಡ್ ರಾಂಚಿಲಾ ಒಂಜಿ.<ref>{{cite news|title=Only 98 cities instead of 100 announced: All questions answered about the smart cities project|url=http://www.firstpost.com/business/why-only-98-cities-instead-of-100-announced-all-questions-answered-about-smart-cities-project-2410576.html|access-date=2017-03-14|work=Firstpost|date=2015-08-28|archive-url=https://web.archive.org/web/20170119003746/http://www.firstpost.com/business/why-only-98-cities-instead-of-100-announced-all-questions-answered-about-smart-cities-project-2410576.html|archive-date=19 January 2017|url-status=live|df=dmy-all}}</ref> ರಾಂಚಿ ಜಿಲ್ಲೆದೊರ್ಮೆ ಬೇತೆ ಬೇತೆ ಬರಿಟ್ ಇಪ್ಪುನ ಅರ್ಬಿಡ್ದಾವರ ರಾಂಚಿಗ್ ಅರ್ಬಿದ ಪೇಂಟೆ- ಜಲಪಾತದ ನಗರ ಪನ್ಪಿ ಪುಗರ್ತೆ ಉಂಡು. ಎಚ್ಚ ಪುದರ್ ಪೋತಿನ ಅರ್ಬಿಲು ಪನ್ನಗ, ಹಿಂದ್ರೂ, ದಸ್ಸಾಮ್, ಜೋನಾ ಸೀತಾ.<ref>{{cite web |title=Which city is known as the City of Waterfalls in India? |url=https://timesofindia.indiatimes.com/life-style/travel/destinations/which-city-is-known-as-the-city-of-waterfalls-in-india/articleshow/126449440.cms |website=timesofindia.indiatimes.com |publisher=TOI |access-date=4 August 2026}}</ref> ಅಂಚನೆ ರಾಂಚಿಡ್ ೧೭ನೇ ಶತಮಾನೊಡು ಕಟ್ಟಾದಿನ ಜಗನ್ನಾಥ ದೇವಸ್ಥಾನೊಲ ಉಂಡು. ==ಪುದರ್== ಮಾನವಶಾಸ್ತ್ರಜ್ಞೆರಾಯಿನ ಶರತ್ ಚಂದ್ರ ರಾಯೆರ್ ಬರೆತಿನ 'ದಿ ಮುಂಡಾಸ್ ಅಂಡ್ ದೇರ್ ಕಂಟ್ರಿ' ಬೂಕುಡು, ಈ ನಗರದ ಪುದರ್ 'ಅರಾಂಚಿ' ಪನ್ಪಿನ ಮುಂಡಾರಿ ಪದೊರ್ದು ಬೈದ್ಂಡ್ ಪಂದ್ ಬರೆತೆರ್. ಅರಾಂಚಿ ಪಂಡ ಕಂಜಿ-ಕೈಕಂಜಿಲೆನ್ ಗಿಡಪರ ಗಲಸುನ ಎಲ್ಯ ಕೋಲು. ನನ ಕೆಲವೆರ್ ಅರಾಂಚಿ ಪಂಡ ವಾರೊಗು ರಡ್ಡ್ ಸರ್ತಿ ನಡಪುನ ಸಂತೆ ಪಂದ್ ಪನ್ಪೆರ್.<ref>{{Cite book|last=Roy|first=Sarat Chandra|title=The Mundas and Their Country|publisher=Jogendra Nath Sarkar at City Book Society|year=1912|location= Calcutta.|pages=142–143 |url=https://books.google.com/books?id=xzIkAAAAMAAJ }} [https://archive.org/details/in.ernet.dli.2015.215493/page/n176/ Alt URL]</ref> ನನೊಂಜಿ ಕತೆತ ಪ್ರಕಾರೊಡು, ರಾಂಚಿದ ಪುದರ್ ಪಹಾರಿ ಮಂದಿರದ ಕೈತಲ್ ಜಾಸ್ತಿ ತೂಯೆರೆ ತಿಕ್ಕೊಂದಿತ್ತಿನ 'ರಿಚಿ' ಪನ್ಪಿನ ಒಂಜಿ ಪಕ್ಕಿರ್ದ್ ಬೈದ್ಂಡ್. ಮುಂಡಾರಿ ಬಾಸೆಡ್ 'ರಿಚಿ' ಪಂಡ ಕಪ್ಪು ಗಿಡು ಪಂದ್ ಅರ್ಥ. ರಾಂಚಿ ನಗರದ ಇತ್ತೆದ ಪ್ರಮುಖ ಜಾಗೆಯಾಯಿನ ಪಹಾರಿ ಮಂದಿರೊನು 'ರಿಚಿ ಬುರು' - ಪಂಡ ಗಿಡುಕ್ಲೆನ ಗುಡ್ಡೆ ಪಂದ್ಲಾ ಲೆಪ್ಪುವೆರ್.<ref>{{Cite book|title=Sylvan tales : stories from the Munda country|last=Samar|first=Bosu Mullick|isbn=9789384465056|location=Kolkata, India|publisher=Adivaani|oclc=935915532|year = 2015}}</ref> ಬ್ರಿಟಿಷ್ ಕ್ಯಾಪ್ಟನ್ ಥಾಮಸ್ ವಿಲ್ಕಿನ್ಸನ್ 'ಅರ್ಚಿ' (ಒರಾಂವ್ ಬುಡಕಟ್ಟು ಗ್ರಾಮ) ಪನ್ಪಿನ ಊರುನು ತನ್ನ ಕೇಂದ್ರ ಕಚೇರಿಯಾದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಸ್ಥಳೀಯ ನಾಗ್ಪುರಿ ಬಾಸೆಡ್ ಅರ್ಚಿ ಪಂಡ ಬೆದ್ರ್‌ದ ಕಾಡ್ (ಬೊಂಬುದ ಕಾಡ್) ಪಂದ್ ಅರ್ಥ. ವಿಲ್ಕಿನ್‌ಸನ್‌ನ ಬಾಯಿಡ್ ಆರ್ಚಿ ರಾಂಚಿ ಆಂಡ್. ಕ್ಯಾಪ್ಟನ್ ವಿಲ್ಕಿನ್ಸನ್ ಕಿಶುನ್‌ಪುರ ಗ್ರಾಮನ್ ರಾಚಿ ಪಂದ್ ಪುದರ್ ಬದಲಾವಣೆ ಮಲ್ತೆರ್. ೧೯೨೭ ಮುಟ್ಟ, ಈ ಜಾಗೆನ್ ರಾಚಿ ಪಂದೇ ಲೆತ್ತೊಂದಿತ್ತೆರ್. ಬ್ರಿಟೀಷ್ ಕಮಿಷನರ್ ಥಾಮಸ್ ವಿಲ್ಕಿನ್ಸನ್ ಆ ಜಾಗೆಡ್ 'ಬಡಾ ತಾಲಾಬ್' ನ್ ಕಟ್ಟುನ ಮೂಲಕ ರಿಚಿ ಬುರುದ ಗುಡ್ಡೆದ ಅಡಿಭಾಗೊಡು ಈ ನಗರ ಸುರುವಾಂಡ್.<ref name="focusmagazine">{{cite web|url=https://www.focusmagazine.in/1800s-when-british-developed-ranchi-as-summer-capital/|title=1800s: When British Developed Ranchi As Summer Capital|website=focusmagazine|date=13 August 2018|access-date=16 January 2020|archive-date=21 May 2020|archive-url=https://web.archive.org/web/20200521213241/https://www.focusmagazine.in/1800s-when-british-developed-ranchi-as-summer-capital/|url-status=dead}}</ref><ref>Cultural Configuration of Ranchi, Lalita Prasad Vidyarthi, Rajendra Behari Lal, India. Planning Commission. Research Programmes Committee, 1969, p. 306.</ref><ref>Journal and Proceedings of the Asiatic Society Of Bengal, Volume 4, No. 11, 1927, "The Etymology of Ranchi", p. 601.</ref> ==ಇತಿಯಾಸೊ== ಪಿರಾಕ್‌ದ ಕಾಲೊಡು, ಇತ್ತೆದ ರಾಂಚಿ ಜಿಲ್ಲೆ ಬೊಕ್ಕ ಕೈತಲ್ದ ಪರಗಣಲೆಗ್ ಸಮ ಆಪಿನ ಈ ಪ್ರದೇಸೊ, ಮುಂಡಾ ಬೊಕ್ಕ ಒರಾಂವ್ ಬುಡಕಟ್ಟು ಜನಾಂಗದಕುಲೆನ ಕೈಟ್ ಇತ್ತ್ಂಡ್. ಅಂಚನೆ, ಈ ಜಾಗೆನ್ ಜಾರ್ಖಂಡ್ ಅತ್ತ್ಂಡ 'ಕಾಡ್‌ದ ಪ್ರದೇಸೊ' ಪಂದ್ ಲೆತ್ತೊಂದಿತ್ತೆರ್. ಪಿರಾಕ್‌ದ ಭಾರತೊಡು ಈ ಇಡೀ ಪ್ರದೇಸೊಗು ನೇರವಾದ್ ಒವ್ವೇ ಅರಸು ವಂಶೊದ ನಿಯಂತ್ರಣ ಇತ್ತಿಜಿ. ಮಹಾಭಾರತದ ಕಾಲೊಡು ರಾಜಗೃಹದ ಚಕ್ರವರ್ತಿಯಾಯಿನ ಜರಾಸಂಧಗ್ ಈ ಪ್ರದೇಸೊದ ಮಿತ್ತ್ ಒಂತೆ ನಿಯಂತ್ರಣ ಇತ್ತ್ಂಡ್. ದುಂಬಗ್, ಇಡೀ ದೇಶೊನು ಒಡಿಶಾ ಮುಟ್ಟ ಗೆಂದಿನ ಮಗಧದ ಮಹಾಪದ್ಮನಂದ ಅಗ್ರಸೇನ ಅರಸುಗು ಈ ಜಾಗೆದ ಮಿತ್ತ್ ಒಂತೆ ಆಡಳಿತದ ಅಧಿಕಾರ ಇತ್ತ್ಂಡ್.<ref>{{cite web |title=EARLY HISTORY |url=https://www.ranchimunicipal.com/Other_Pages.aspx?wid=City_History |website=ranchimunicipal.com |publisher=IT SECTION, RMC |access-date=4 August 2026}}</ref> ==ಉಲ್ಲೇಕ== {{reflist}} [[ವರ್ಗೊ:ಭಾರತೊದ ರಾಜ್ಯ ರಾಜಧಾನಿಲು]] [[ವರ್ಗೊ:ತುಳು ವಿಕಿಪೀಡಿಯ 10ನೇ ವರ್ಸಾಂತ್ಯೊ]] d8vyhy6msvfj0ic2fdldk47fk07ehp3 ಬೋಂಡ (ತಿಂಡಿ) 0 27721 366099 364147 2026-08-21T02:14:50Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366099 wikitext text/x-wiki == ಬೋಂಡ (ತಿಂಡಿ) == [[ಫೈಲ್:The_real_South_Indian_Bonda.jpg|thumb|The real South Indian Bonda]] ಬೋಂಡಾ ಪಂಡ ಒಂಜಿ ಡೀಪ್-ಫ್ರೈಡ್ ದಕ್ಷಿಣ [[ಭಾರತ]]ದ ಆಲೂಗಡ್ಡೆದ ತಿಂಡಿ.ಉಂದೆಟ್ ಬೇತೆ ಬೇತೆ ಪ್ರದೇಶೊಲೆಡ್ ಬೇತೆ ಬೇತೆ ಸಿಹಿ ಬೊಕ್ಕ ರುಚಿಯಾದ್ ಉಪ್ಪುಂಡು. ಅತ್ಯಂತ ಸಾಮಾನ್ಯವಾದ್ ಅಲೂ ಬೊಂಡಾ (ಆಲೂಗಡ್ಡೆ ಬೊಂಡಾ), ಬೊಕ್ಕ ಇತರ ಪ್ರದೇಶ-ನಿರ್ದಿಷ್ಟ ಬದಲಾವಣೆಲು ಸಿಹಿ ಆಲೂಗಡ್ಡೆ, ಟ್ಯಾಪಿಯೋಕಾ, ತುರಿದ ಅನಾನಸ್, ಹಸಿರು ಬಟಾಣಿ, ಪನೀರ್, ಅತ್ತಂದೆ ಅರಿ ಸೇರ್ದ್ ಇತರ ಪದಾರ್ಥೊಲೆನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು. <ref>https://www.hindustantimes.com/brunch/take-pride-in-the-bonda-or-pakora-it-is-our-gift-to-the-world/story-khxwM10l0NT8HLZ8ik9G7I.html</ref> == ಚರಿತ್ರೆ == [[ಫೈಲ್:Bonda_soup.jpg|thumb|Bonda served with hot dal soup]] ಇತ್ತೆದ [[ಕರ್ನಾಟಕ|ಕರ್ನಾಟಕೊ]]ಡ್ದ್ ಆಳ್ವಿಕೆ ಮಲ್ತಿನ ಸೋಮೇಶ್ವರ ಸಂಕಲನ ಮಲ್ತಿನ 12ನೇ ಶತಮಾನೊದ ಸಂಸ್ಕೃತ ವಿಶ್ವಕೋಶೊ ಆಯಿನ ಮನಸೊಲ್ಲಾಸೊಡು ಬೊಂಡೊದ (ಪರಿಕಾ ಆದ್) ಒಂಜಿ ಪಾಕವಿಧಾನೊನು ಉಲ್ಲೇಖ ಮಲ್ತೆರ್. <ref>{{Cite web |url=https://food.ndtv.com/lists/10-best-karnataka-kannada-recipes-1275758 |title=Archive copy |accessdate=2026-08-09 |archivedate=2026-01-23 |archive-url=https://web.archive.org/web/20260123022134/https://food.ndtv.com/lists/10-best-karnataka-kannada-recipes-1275758 |url-status=dead }}</ref> == ಅಟ್ಟಣೆ == [[ಫೈಲ್:Bonda_(1).jpg|thumb|Bonda (1)]] [[ಮಂಗಳೂರು]] ಬೊಂಡ ಬೊಂಡದ ಸೂಪ್: ಉರಡ್ ದಾಲ್ ಬೊಂಡನ್ ಬಿಸಿ ದಾಲ್ ಸೂಪ್ ದ ಒಟ್ಟುಗು ಬಲಸುವೆರ್ ಮಸಾಲೆದ ಬೊಂಡಾ ಮಲ್ಪುನ ಪ್ರಕ್ರಿಯೆಡ್ ಆಲೂಗಡ್ಡೆದ ತುಂಬುನವು ಉಂಡು, ಉಂದೆನ್ ಗ್ರಾಮ್ ಹಿಟ್ಟುದ ಪಿಂಗಾರೊಡು ಮುರ್ಕುದು ಡೀಪ್ ಫ್ರೈ ಮಲ್ಪುವೆರ್. <ref>https://udupi-recipes.com/vegetable-bonda-veg-bonda-recipe</ref> ಬೊಂಡೊಗು ಒಂಜಿ ಸಿಹಿ ಬೊಕ್ಕ ಒಂಜಿ ಮಸಾಲೆದ ರೂಪಾಂತರ ಉಂಡು. ಕೇರಳೊಡು ಕೆಲವು ಪ್ರಾದೇಶಿಕ ರೂಪಾಂತರೊಲು ಆಲೂಗಡ್ಡೆನ್ ಟ್ಯಾಪಿಯೋಕಾ (ಟ್ಯಾಪಿಯೋಕಾ ಬೊಂಡಾ) ಅತ್ತ್ಂಡ ಸಿಹಿ ಆಲೂಗಡ್ಡೆ ಬೊಕ್ಕ ಕೆಲವು ಈರುಳ್ಳಿ, ಗಟ್ಟಿಯಾದ್ ಬೇಯ್ಪಾಯಿನ ಮೊಟ್ಟೆ (ಮುಟ್ಟ ಬೊಂಡ), ಮಸಾಲೆ, ಕಡ್ಲೆಕಾಯಿ ಬೊಕ್ಕ ಬೇತೆ ಸಾಮಗ್ರಿಲೆನ್ ಪಾಡ್ದ್ ಬದಲಾವಣೆ ಮಲ್ಪುವೆರ್. [[ತಮಿಳುನಾಡು]]ಡ್ ಬೊಂಡೊನು ಕಪ್ಪು ಗ್ರಾಮೆದ (ಉಂದು) ಪಿಂಗಾರೊಡು ಮಲ್ಪುವೆರ್. [[ಆಂಧ್ರ ಪ್ರದೇಶ|ಆಂಧ್ರ ಪ್ರದೇಶೊ]]ಡು ಉಂದೆನ್ ಬೊಂಡಾಲು ಅತ್ತ್ಂಡ ಪೂರ್ಣಾಲು ಪಂದ್ ಲೆಪ್ಪುವೆರ್. ತರಕಾರಿ ಬೊಂಡ ಪಂಡ ಉಡುಪಿ ಪಾಕಪದ್ಧತಿದ ಒಂಜಿ ಖಾದ್ಯ, ಉಂದೆಟ್ ತಾಜಾ ಪಚ್ಚೆ ಬಟಾಣಿ ಬೊಕ್ಕ ಫ್ರೆಂಚ್ ಬೀನ್ಸ್, ಕ್ಯಾರೆಟ್ ಬೊಕ್ಕ ಕೊತ್ತಂಬರಿ ಎಲೆಲೆನಂಚಿನ ಮೃದುವಾದ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ತರಕಾರಿಲೆನ್ ತುಂಬುನವು ಆದ್ ಬಳಕೆ ಮಲ್ಪುವೆರ್. [[ಗೋಳಿ ಬಜೆ]] (ಮಂಗಳೂರು ಬೊಂಡ ಅತ್ತ್ಂಡ ಮಂಗಳೂರು ಬಜ್ಜಿ ಪಂದ್ ಲಾ ಲೆಪ್ಪುವೆರ್) ಕರ್ನಾಟಕೊದ ಬೇತೆ ಒಂಜಿ ರೂಪ. ಈ ಬೊಂಡೊನು, ಆಂಡಲಾ, ಮೈದಾ ಹಿಟ್ಟುಡ್ದ್ (ರಿಫೈನ್ಡ್ ಹಿಟ್ಟು) ಮಲ್ಪುವೆರ್. == ಉಲ್ಲೇಕೊ == <nowiki>[[ವರ್ಗ:Kerala Culture Month 2026]]</nowiki> kv60bky2tbluot4ar21o0cvb16ogerz ಇಂಜಿಪುಳಿ 0 27730 366094 364162 2026-08-20T23:53:23Z InternetArchiveBot 4316 Rescuing 0 sources and tagging 1 as dead.) #IABot (v2.0.9.5 366094 wikitext text/x-wiki [[ಫೈಲ್:Puliyinchi_with_a_face.jpg|thumb|Puliyinchi with a face]] ಇಂಜಿಪುಳಿ (ಮಲಯಾಳಂ: ഇഞ്ചിപുളി), ಪುಳಿ ಇಂಜಿ ಅತ್ತಂದೆ ಇಂಜಿ ಕರಿ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಉಂದು ಒಂಜಿ ಸಿಹಿ, ಹುಳಿ ಬೊಕ್ಕ ಮಸಾಲೆದ [[ಕೇರಳ]]ದ ಮಸಾಲೆ ಆದುಂಡು, ಉಂದೆನ್ ಮುಖ್ಯವಾದ್ ಶುಂಠಿ, ತಾರಾಯಿ, ಹಸಿರು ಮೆಣಸಿನಕಾಯಿ ಬೊಕ್ಕ ಬೆಲ್ಲೊಡ್ದ್ ಮಲ್ಪುವೆರ್.ಉಂದು ಕೇರಳ [[ಸದ್ಯ|ಸದ್ಯೊ]]ಡು (ಭೋಜನ) ಪ್ರಮಾಣಿತ ವಸ್ತುಲೆಡ್ ಒಂಜಿ ಆದುಂಡು ಬೊಕ್ಕ [[ಓಣಮ್|ಓಣಂ]] ಡ್ ಸರ್ವ್ ಮಲ್ಪುನ ವಣಸ್ ದ ಒಂಜಿ ಅಗತ್ಯವಾಯಿನ ಅಂಶ ಪಂಡ್ದ್ ಪರಿಗಣಿಸವೆರ್.ಕೇರಳೊಗು ಮಸ್ತ್ ಸಂಬಂಧ ಉಂಡು ಆಂಡಲಾ, ಉಂದೆನ್ ನೆರೆಕರೆತ [[ತಮಿಳುನಾಡು]]ಡ್ ಲಾ ತಯಾರ್ ಮಲ್ಪುವೆರ್, ಅಲ್ಪ ಉಂದೆನ್ ಮದಿಮೆ ಬೊಕ್ಕ ಬೇತೆ ಕಾರ್ಯಕ್ರಮೊಲೆಡ್ ಸರ್ವ್ ಮಲ್ಪುವೆರ್<ref>https://rakskitchen.net/kerala-puli-inji-recipe/</ref> == ವ್ಯಾಕರಣ == ಈ ಪುದರ್ ದ್ರಾವಿಡ ಭಾಷೆದ ಇಂಜಿ ("[[ಶುಂಠಿ|ಶುಂಠಿ")]] ಬೊಕ್ಕ [[ಪುಳಿ ಕಜಿಪು|ಪುಳಿ]] ("ತಮರಿ") ಪನ್ಪಿನ ಶಬ್ದೊಲೆನ ಸಂಯೋಜನೆ, ಈ ಖಾದ್ಯೊದ ರಡ್ಡ್ ವ್ಯಾಖ್ಯಾನ ಪದಾರ್ಥೊಲು; ಈ ಪುದರ್ ದ ರಡ್ಡ್ ಕ್ರಮೊಲು, ಇಂಜಿಪುಳಿ ಬೊಕ್ಕ ಪುಳಿ ಇಂಜಿ, ಕೇರಳದ ಬೇತೆ ಬೇತೆ ಭಾಗೊಲೆಡ್ ಪರಸ್ಪರ ಬದಲಾವಣೆಡ್ ಬಳಕೆ ಆಪುಂಡು.<ref>https://www.onmanorama.com/food/mrs-k-m-mathew/2018/06/30/inji-curry-kerala-recipes-onam-sadya-feast-injikari.html</ref> == ಅಟ್ಟಣೆ == ಶುಂಠಿನ್ ಸಿಪ್ಪೆ ದೆತ್ತ್ ದ್, ಮೃದುವಾದ್ ಕಡ್ತ್ ದ್ ಅತ್ತ್ಂಡ ಕಡ್ತ್ ದ್, ಕಂದು ಬಣ್ಣೊಗು ಬರ್ಪಿನ ಮುಟ್ಟ ತಾರಾಯಿದ ಎಣ್ಣೆಡ್ ಫ್ರೈ ಮಲ್ಪುವೆರ್, ಉಂದು ಅಯಿತ ತೀಕ್ಷ್ಣತೆನ್ ಮೃದು ಮಲ್ಪುಂಡು. ಅಪಗ ಉಂದೆನ್ ತಾರಾಯಿದ ಸಾರ, ಬೆಲ್ಲ ಬೊಕ್ಕ ಪಚ್ಚೆ ಮೆಣಸಿನಕಾಯಿಡ್ ಕುದಿಪಾದ್, ಸಾಸಿವೆ ಬೀಜೊಲು, ಮೆಂತೆ, ಒಣಸ್ ದ ಕೆಂಪು ಮೆಣಸಿನಕಾಯಿ ಬೊಕ್ಕ ಕರಿಬೇವುದ ಇರೆನ್ ಅರಿ ಬೊಕ್ಕ ಉಪ್ಪುದೊಟ್ಟುಗು ಪಾಡ್ದ್ ಮಸಾಲೆ ಮಲ್ಪುವೆರ್.ಸಿಹಿ, ಹುಳಿ, ಉಪ್ಪು ಬೊಕ್ಕ ಉಷ್ಣೊದ ನಾಲ್ ರುಚಿಲು ಸಮತೋಲನ ಆಪಿನ ದಪ್ಪ, ಜಾಮ್ ಲೆಕೊದ ಸ್ಥಿರತೆಗ್ ಬರ್ಪಿನ ಮುಟ್ಟ ಮಿಶ್ರಣೊನು ಬೇಯಿಪಾವೆರ್.ಅಯಿಟ್ ಮಸ್ತ್ ತೆಂಗಿನಕಾಯಿ, ಬೆಲ್ಲ ಬೊಕ್ಕ ಎಣ್ಣೆದ ಅಂಶ ಉಪ್ಪುನೆರ್ದಾತ್ರ, ಇಂಜಿಪುಲಿ ಎಡ್ಡೆ ರೀತಿಡ್ ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಒಂಜಿ ದಿನ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ದುಂಬು ಮಲ್ತ್ ದ್ ಉಪ್ಪಿನಕಾಯಿದಲೆಕ ಸಂಗ್ರಹ ಮಲ್ಪುವೆರ್.ಈ ಕೇಂದ್ರೀಕೃತ, ದೀರ್ಘಕಾಲೊಗು ಉಪ್ಪುನ ಸ್ವಭಾವೊಗು ಕಾರಣವಾದ್, ಉಂದೆನ್ ಬೇತೆ ಬೇತೆ ಮೂಲೊಲೆಡ್ ಕರಿ, ಉಪ್ಪಿನಕಾಯಿ ಅತ್ತಂಡ ಚಟ್ನಿ ಪಂಡ್ದ್ ವರ್ಣನೆ ಮಲ್ತೆರ್.<ref>https://gulfnews.com/food/cooking-cuisines/video-recipe-for-kerala-style-spicy-inji-puli-or-ginger-chutney-1.1660838486905</ref> == ಸೇವನೆ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಸದ್ಯ ಬಾಳೆಹಣ್ಣುದ ಎಲೆಡ್, ಇಂಜಿಪುಲಿನ್ ಒಂಜಿ ನಿಶ್ಚಿತ ಸ್ಥಾನೊಡು ಬಾರಿ ಕಮ್ಮಿ ಪ್ರಮಾಣೊಡು, ಸಾಂಪ್ರದಾಯಿಕವಾದ್ ಮಿತ್ತ್ ದ ಮೂಲೆಡ್, ಬೊಕ್ಕ ಊಟದ ಶ್ರೀಮಂತ ಖಾದ್ಯೊಲೆನ್ ಕತ್ತರಿಸವುನ ಬೊಕ್ಕ ಸಮತೋಲನ ಮಲ್ಪುನ ನಿಟ್ಟಿಡ್ ಬಾಯಿದ ನಡುಟು ಎಲ್ಯ ಪ್ರಮಾಣೊಡು ತಿನ್ಪೆರ್.ಉಂದು ಮಲ್ಲ, ಪ್ರಧಾನವಾದ್ ಸಸ್ಯಾಹಾರಿ ಭೋಜನೊಗು ಜೀರ್ಣಕಾರಿ ಜೊತೆಯಾದ್ ಲಾ ಮೌಲ್ಯೊಡು ಉಂಡು.ಓಣಂ ದಾಂತೆ, ಉಂದು [[ಬಿಸು ಪರ್ಬೊ|ವಿಶು]]ಗು ಸರ್ವ್ ಮಲ್ಪುನ ಸದ್ಯೊಡು ಬೊಕ್ಕ ಮದಿಮೆ ಬೊಕ್ಕ ಬೇತೆ ಆಚರಣೆದ ಸಂದರ್ಭೊಲೆಡ್ ತೋಜುಂಡು, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಸಾದಾ ಅರಿ, ಮೈದಾ ಅರಿ ಬೊಕ್ಕ ಬೇತೆ ದಕ್ಷಿಣ ಭಾರತದ ಖಾದ್ಯೊಲೆನೊಟ್ಟುಗು ತಿನ್ಪೆರ್. == ಜಾನಪದೊಡು == ಕೇರಳೊಡು ಪುನರಾವರ್ತಿತವಾದ್ ಉಪ್ಪುನ ಒಂಜಿ ಜಾನಪದ ಕತೆ ಈ ಖಾದ್ಯೊನು ವಿದ್ವಾಂಸೆ ವರರುಚಿಗ್ ಕಟ್ಟ್ ದ್ ಕೊರ್ಪುಂಡು. ಒರಿ ಜವನ್ಯೆನ ಜಾಣ್ಮೆನ್ ಪರೀಕ್ಷೆ ಮಲ್ತ್ದ್, ಸ್ನಾನ ಮಲ್ತ್ದ್ ಬರ್ಪಿನ ಪೊರ್ತುಗು ನೂದು ಪಾತ್ರೆಲೆನ್ ಸರ್ವ್ ಮಲ್ಪೆರೆ ಕೇನುವೆರ್; ಅಲ್ ಅರೆಗ್ ಇಂಜಿ ಕರಿತ ಒಟ್ಟುಗು ಅರಿನ್ ಕೊರ್ಪೆರ್, ಬೊಕ್ಕ ಒಂಜಿ ರುಚಿನ್ ನೂದುಗು ನಿಲೆ ಮಲ್ಪುವೆರ್. ಉಂದೆಡ್ದ್ ಈ ಖಾದ್ಯೊನು ನೂದು, ಅತ್ತ್ಂಡ ಕೆಲವು ಪಾತೆರೊಡು ನೂದು ಒಂಜಿ, ಕರಿಲೆನ ಸಮ ಪಂಡ್ದ್ ಪನ್ಪೆರ್. ಇಂಜಿ ಕರಿ ಕೇರಳೊಡು ಔಷಧೀಯ ಮೌಲ್ಯದ ಶ್ರೇಯೊಡು ಉಂಡು ಬೊಕ್ಕ ಅಜೀರ್ಣೊಗು ಜಾನಪದ ಮರ್ದ್ ಆದ್ ಸೇವನೆ ಮಲ್ಪುವೆರ್.<ref>https://masalachilli.com/puli-inji-kerala-sweet-and-sour-ginger-pickle-for-sadya/{{Dead link|date=August 2026 |bot=InternetArchiveBot |fix-attempted=yes }}</ref> <nowiki>[[ವರ್ಗ:Kerala Culture Month 2026]]</nowiki> jsxct0u13t8692qoutnooev2py3q3sp ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ 4 27750 366089 365841 2026-08-20T16:11:04Z Pragathi. BH 6361 /* ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ */ 366089 wikitext text/x-wiki '''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ'''<br> {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | [[File:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ.png|thumb|'''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ''']] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಲೆನ್ ಒಟ್ಟು ಮಲ್ಪುನ ಒಂಜಿ ಬಹುಭಾಷಿಕ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆದ ಅಭಿಯಾನ. ರಡ್ಡ್ ದೇಶೊಲೆನ ಸಮುದಾಯೊಲೆಡ್ಲಾ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಸಾಮಾನ್ಯ ಆಸಕ್ತಿ ಬೊಕ್ಕ ಮೆಚ್ಚುಗೆನ್ ಆಚರಣೆ ಮಲ್ಪುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು.<br> ಈ ಅಭಿಯಾನ '''15 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಭಾರತದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಡ್ ಸುರು ಆಪುಂಡು, ಬೊಕ್ಕ '''24 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಉಕ್ರೇನ್ ದೇಶದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಗ್ ಮುಗಿವುಂಡು. ಬೇತೆ ಬೇತೆ ಭಾಷೆಲೆನ ನಡುಟು ಜ್ಞಾನ ವಿನಿಮಯೊನು ಬುಲೆಪಾವುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು. ಉಕ್ರೇನ್ ದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಭಾರತೀಯ ಭಾಷೆದ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಂಚನೆ, ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಗು ಭಾರತೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾದ್ ಉಪ್ಪುನ ಬೇತೆ ಅರ್ಹ ವಿಷಯೊಲೆನ್ ಭಾಗವಹಿಸುನಕುಲು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. == ಪಾಲ್ ಪಡೆಯುನು ಎಂಚ == * ಪಾಲ್ ಪಡೆಯಿನಕುಲು ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ದ್ ಒಂಜಿ ವಿಷಯೊನು ಆಯ್ಕೆ ಮಲ್ಪೊಲಿ. * [https://meta.wikimedia.org/w/index.php?title=Special:EventDetails/4246&tab=WorklistPanel ಅಭಿಯಾನದ ಕೆಲಸೊದ ಪಟ್ಟಿನ್ ತೂಲೆ →] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ಡ್ ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ತಾಣೊಲೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಸೂಚಿತ ಲೇಖನೊಲೆನ್ ಈ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಉಂಡು. * ೧೫ ಬೊಕ್ಕ ೨೪ ಆಗಸ್ಟ್ ೨೦೨೬ ನಡುಟು ಒಂಜಿ ಲೇಖನೊನು ರಚನೆ ಮಲ್ಪುಲೆ ಅತ್ತುಡ ಸುಧಾರಿಸಾಲೆ. == ಅಂಕೊದ ವ್ಯವಸ್ಥೆ == ಅಭಿಯಾನದ ಅವಧಿಡ್ ಅರ್ಹ ಕೊಡುಗೆ ಕೊರ್ಪಿನ ಭಾಗವಹಿಸುವವರೆಗ್ ಅಂಕೊಲೆನ್ ಕೊರ್ಪೆರ್: *ಅರ್ಹತೆ ಪಡೆಯಿನ ಪೊಸ ಅತ್ತಂದೆ ಇತ್ತೆದ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಗು ಸೇರಾಯಿನ ಪ್ರತಿ 200 ಪದೊಕುಲೆಗ್ +1 ಅಂಕ. *ಗಣನೀಯವಾದ್ ಸುಧಾರಣೆ ಆಯಿನ ಪ್ರತಿಯೊಂಜಿ ಲೇಖನೊಗು +2 ಅಂಕೊಲು. ಗಮನಾರ್ಹ ಸುಧಾರಣೆ ಪಂಡ ಉಲ್ಲೇಕೊಲೆದೊಟ್ಟುಗು ಸಾಕಷ್ಟು ವಿಶ್ವಕೋಶದ ಮಾಹಿತಿನ್ ಒಳಗೊಂಡುದುಂಡು, ಲೇಖನದ ರಚನೆ ಅತ್ತಂದೆ ಓದರೆ ಸಾಮರ್ಥ್ಯನ್ ಗಮನಾರ್ಹವಾದ್ ಸುಧಾರಿಸವುಂಡು, ಗಮನಾರ್ಹವಾದ್ ಕಳೆದ್ ಪೋತಿನ ಮಾಹಿತಿನ್ ಸೇರಾವುಂಡು, ಅತ್ತಂದೆ ಅತೇ ಮಹತ್ವೊದ ಸುಧಾರಣೆನ್ ಮಲ್ಪುಂಡು. == ಅಂಕೊಲೆಗ್ ಅರ್ಹತೆ ಪಡೆಯೆರೆ == *ಒಂಜಿ ಪೊಸತ್ ರಚನೆ ಆಯಿನ ಲೇಖನೊಡು ಕನಿಷ್ಠ 500 ಪದೊಕುಲೆನ ಓದೆರೆ ಆಪಿನ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊಲು ಉಪ್ಪೊಡು. * ಒಂಜಿ ಇತ್ತೆದ ಲೇಖನೊಗು ಸುಧಾರಣೆ ಮಲ್ಪುನಗ ಕನಿಷ್ಠ 300 ಪದೊಕುಲೆನ ಪೊಸ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊನು ಸೇರಾವೊಡು. ಆಂಡಲಾ, ಸುಧಾರಣೆ ಮುಖ್ಯವಾದ್ ಮಹತ್ವದ ಪುನರ್ರಚನೆ, ಭಾಷಾ ಸಂಪಾದನೆ, ಮೂಲೊಲೆನ ಸೇರ್ಪಡೆ, ಅತ್ತಂದೆ ಬೇತೆ ಸ್ಪಷ್ಟವಾದ್ ಪ್ರದರ್ಶಿಸವೊಲಿಯಾಯಿನ ಮಹತ್ವದ ಸುಧಾರಣೆನ್ ಒಳಗೊಂಡಿತ್ತ್ಂಡ ಈ ನಿಯಮೊನು ಮನ್ನಾ ಮಲ್ಪೊಲಿ. *ಮಾತಾ ಅಗತ್ಯೊದ ಮುಖ್ಯ ಮಾಹಿತಿಲು ವಿಶ್ವಾಸಾರ್ಹ ಮೂಲೊಲೆನ್ ಆಧಾರವಾದ್ ಉಪ್ಪೊಡು. * ಮಾತ ಕೊಡುಗೆಲು ಸಮ್ಮಂದಪಟ್ಟಿನ ಬಾಸೆದ ವಿಕಿಪೀಡಿಯೊದ ನೀತಿ ಬೊಕ್ಕ ಮಾರ್ಗಸೂಚಿಲೆನ್ ಪಾಲನೆ ಮಲ್ಪೊಡು. *ಎಲ್ಯ/ಕಿನ್ಯ ಸಂಪಾದನೆಲು — ಉದಾಹರಣೆಗ್ ಮುದ್ರಣ ತಪ್ಪುಲೆನ್ ಸರಿ ಮಲ್ಪುನವು, ಲೇಔಟ್ ನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು, ವಿಭಾಗೊಲೆನ್ ಸೇರಾವುನವು, ಅತ್ತಂದೆ ಪ್ರತ್ಯೇಕ ಲಿಂಕ್ ಲೆನ್ ಸೇರಾವುನವು — ಉಂದೆನ್ ಮಾತ್ರ ಆಧಾರ ಮಲ್ತ್ದ್ ಮಹತ್ವೊದ ಸುಧಾರಣೆಲೆನ್ ಪರಿಗಣಿಸರೆ ಆಪುಜಿ. == ಬಹುಮಾನೊಲು ಬೊಕ್ಕ ಗೌರವೊಲು == ಪ್ರಚಾರದ ನಿಯಮೊಲೆನ್ ಪಾಲನೆ ಮಲ್ಪುನ ಭಾಗವಹಿಸುವವೆರ್ ಈ ತಿರ್ತುದ ಗೌರವೊಲೆಗ್ ಅರ್ಹರಾದುಪ್ಪುವೆರ್: *ಮಾತಾ ಅರ್ಹ ಭಾಗವಹಿಸುನಕಲೆಗ್ ಡಿಜಿಟಲ್ ಪ್ರಮಾಣ ಪತ್ರೊಲು. *ಎಚ್ಚಿನ ಅಂಕೊಲೆನ್ ಪಡೆಯಿನ ಮೂಜಿ ಜನಕ್ಲೆಗ್ ಆನ್ ಲೈನ್ ಶಾಪಿಂಗ್ ವೌಚರ್ ಲು. ಭಾರತೀಯ ಬೊಕ್ಕ ಉಕ್ರೇನಿಯನ್ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ಶ್ರೇಣಿಲೆನ್ ಪರಿಗಣಿಸೊಡು: # ಸುರುತ ಸ್ಥಾನ — $100 / ₹10,000 #ರಡ್ಡನೆ ಸ್ಥಾನ — $50 / ₹5,000 #ಮೂಜಿನೇ ಸ್ಥಾನ — $30 / ₹3,000 ಅಭಿಯಾನದ ಫಲಿತಾಂಶೊಲೆನ್ ಪರಿಶೀಲನೆ ಮಲ್ತ್ ದ್ ಖಚಿತಪಡಿಸಾಯಿನ ಬುಕ್ಕೊ, ಸಂಘಟಕೆರ್ ಅರ್ಹ ಭಾಗವಹಿಸುವವರೆನ್ ಸಂಪರ್ಕ ಮಲ್ಪುವೆರ್. == ಸಂಘಟಕೆರ್ == ಹರಿಪ್ರಸಾದ್ ಶೆಟ್ಟಿ ([[User:Hariprasad Shetty10 |Hariprasad Shetty]]) == ಉಕ್ರೇನ್ ಕುರಿತಾಯಿನ ಲೇಕೊನೊದ ಪಟ್ಟಿ == https://meta.wikimedia.org/wiki/Event:Heritage_Across_Borders/Article_List ==ಇರೆನ ಲೇಕೊನೊಲೆನ್ ಮುಲ್ಪ ಸೇರಾಲೆ== https://wikieval.toolforge.org/contests/5 ==ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ== #[[ಬಳಕೆದಾರೆ:Hariprasad Shetty10|Hariprasad Shetty10]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Hariprasad Shetty10|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೬, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Santhosh Notagar99|Santhosh Notagar99]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Santhosh Notagar99|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೭, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೦೬:೦೩, ೧೫ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೩:೦೪, ೧೯ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Pragathi. BH|Pragathi. BH]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Pragathi. BH|ಪಾತೆರ್ಲೆ]]) ೨೧:೪೧, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ಸಹಾಯೊ ಬೊಕ್ಕ ಪ್ರಶ್ನೆಲು == ಅಭಿಯಾನೊಗು ಸಂಬಂಧಪಟ್ಟಿನ ಸಾಮಾನ್ಯ ಪ್ರಶ್ನೆಲೆಗ್ ಅತ್ತಂದೆ ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್, ದಯಮಲ್ತ್ ಅಭಿಯಾನೊದ ಪಾತೆರೊದ ಪುಟೊನು ಬಳಕೆ ಮಲ್ಪುಲೆ. [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ]] |} bi2xpjlr7581fy74giydgnszejl5vvd 366147 366089 2026-08-21T07:49:18Z Sannidhishetty 9770 /* ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ */ 366147 wikitext text/x-wiki '''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ'''<br> {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | [[File:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ.png|thumb|'''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ''']] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಲೆನ್ ಒಟ್ಟು ಮಲ್ಪುನ ಒಂಜಿ ಬಹುಭಾಷಿಕ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆದ ಅಭಿಯಾನ. ರಡ್ಡ್ ದೇಶೊಲೆನ ಸಮುದಾಯೊಲೆಡ್ಲಾ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಸಾಮಾನ್ಯ ಆಸಕ್ತಿ ಬೊಕ್ಕ ಮೆಚ್ಚುಗೆನ್ ಆಚರಣೆ ಮಲ್ಪುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು.<br> ಈ ಅಭಿಯಾನ '''15 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಭಾರತದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಡ್ ಸುರು ಆಪುಂಡು, ಬೊಕ್ಕ '''24 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಉಕ್ರೇನ್ ದೇಶದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಗ್ ಮುಗಿವುಂಡು. ಬೇತೆ ಬೇತೆ ಭಾಷೆಲೆನ ನಡುಟು ಜ್ಞಾನ ವಿನಿಮಯೊನು ಬುಲೆಪಾವುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು. ಉಕ್ರೇನ್ ದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಭಾರತೀಯ ಭಾಷೆದ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಂಚನೆ, ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಗು ಭಾರತೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾದ್ ಉಪ್ಪುನ ಬೇತೆ ಅರ್ಹ ವಿಷಯೊಲೆನ್ ಭಾಗವಹಿಸುನಕುಲು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. == ಪಾಲ್ ಪಡೆಯುನು ಎಂಚ == * ಪಾಲ್ ಪಡೆಯಿನಕುಲು ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ದ್ ಒಂಜಿ ವಿಷಯೊನು ಆಯ್ಕೆ ಮಲ್ಪೊಲಿ. * [https://meta.wikimedia.org/w/index.php?title=Special:EventDetails/4246&tab=WorklistPanel ಅಭಿಯಾನದ ಕೆಲಸೊದ ಪಟ್ಟಿನ್ ತೂಲೆ →] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ಡ್ ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ತಾಣೊಲೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಸೂಚಿತ ಲೇಖನೊಲೆನ್ ಈ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಉಂಡು. * ೧೫ ಬೊಕ್ಕ ೨೪ ಆಗಸ್ಟ್ ೨೦೨೬ ನಡುಟು ಒಂಜಿ ಲೇಖನೊನು ರಚನೆ ಮಲ್ಪುಲೆ ಅತ್ತುಡ ಸುಧಾರಿಸಾಲೆ. == ಅಂಕೊದ ವ್ಯವಸ್ಥೆ == ಅಭಿಯಾನದ ಅವಧಿಡ್ ಅರ್ಹ ಕೊಡುಗೆ ಕೊರ್ಪಿನ ಭಾಗವಹಿಸುವವರೆಗ್ ಅಂಕೊಲೆನ್ ಕೊರ್ಪೆರ್: *ಅರ್ಹತೆ ಪಡೆಯಿನ ಪೊಸ ಅತ್ತಂದೆ ಇತ್ತೆದ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಗು ಸೇರಾಯಿನ ಪ್ರತಿ 200 ಪದೊಕುಲೆಗ್ +1 ಅಂಕ. *ಗಣನೀಯವಾದ್ ಸುಧಾರಣೆ ಆಯಿನ ಪ್ರತಿಯೊಂಜಿ ಲೇಖನೊಗು +2 ಅಂಕೊಲು. ಗಮನಾರ್ಹ ಸುಧಾರಣೆ ಪಂಡ ಉಲ್ಲೇಕೊಲೆದೊಟ್ಟುಗು ಸಾಕಷ್ಟು ವಿಶ್ವಕೋಶದ ಮಾಹಿತಿನ್ ಒಳಗೊಂಡುದುಂಡು, ಲೇಖನದ ರಚನೆ ಅತ್ತಂದೆ ಓದರೆ ಸಾಮರ್ಥ್ಯನ್ ಗಮನಾರ್ಹವಾದ್ ಸುಧಾರಿಸವುಂಡು, ಗಮನಾರ್ಹವಾದ್ ಕಳೆದ್ ಪೋತಿನ ಮಾಹಿತಿನ್ ಸೇರಾವುಂಡು, ಅತ್ತಂದೆ ಅತೇ ಮಹತ್ವೊದ ಸುಧಾರಣೆನ್ ಮಲ್ಪುಂಡು. == ಅಂಕೊಲೆಗ್ ಅರ್ಹತೆ ಪಡೆಯೆರೆ == *ಒಂಜಿ ಪೊಸತ್ ರಚನೆ ಆಯಿನ ಲೇಖನೊಡು ಕನಿಷ್ಠ 500 ಪದೊಕುಲೆನ ಓದೆರೆ ಆಪಿನ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊಲು ಉಪ್ಪೊಡು. * ಒಂಜಿ ಇತ್ತೆದ ಲೇಖನೊಗು ಸುಧಾರಣೆ ಮಲ್ಪುನಗ ಕನಿಷ್ಠ 300 ಪದೊಕುಲೆನ ಪೊಸ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊನು ಸೇರಾವೊಡು. ಆಂಡಲಾ, ಸುಧಾರಣೆ ಮುಖ್ಯವಾದ್ ಮಹತ್ವದ ಪುನರ್ರಚನೆ, ಭಾಷಾ ಸಂಪಾದನೆ, ಮೂಲೊಲೆನ ಸೇರ್ಪಡೆ, ಅತ್ತಂದೆ ಬೇತೆ ಸ್ಪಷ್ಟವಾದ್ ಪ್ರದರ್ಶಿಸವೊಲಿಯಾಯಿನ ಮಹತ್ವದ ಸುಧಾರಣೆನ್ ಒಳಗೊಂಡಿತ್ತ್ಂಡ ಈ ನಿಯಮೊನು ಮನ್ನಾ ಮಲ್ಪೊಲಿ. *ಮಾತಾ ಅಗತ್ಯೊದ ಮುಖ್ಯ ಮಾಹಿತಿಲು ವಿಶ್ವಾಸಾರ್ಹ ಮೂಲೊಲೆನ್ ಆಧಾರವಾದ್ ಉಪ್ಪೊಡು. * ಮಾತ ಕೊಡುಗೆಲು ಸಮ್ಮಂದಪಟ್ಟಿನ ಬಾಸೆದ ವಿಕಿಪೀಡಿಯೊದ ನೀತಿ ಬೊಕ್ಕ ಮಾರ್ಗಸೂಚಿಲೆನ್ ಪಾಲನೆ ಮಲ್ಪೊಡು. *ಎಲ್ಯ/ಕಿನ್ಯ ಸಂಪಾದನೆಲು — ಉದಾಹರಣೆಗ್ ಮುದ್ರಣ ತಪ್ಪುಲೆನ್ ಸರಿ ಮಲ್ಪುನವು, ಲೇಔಟ್ ನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು, ವಿಭಾಗೊಲೆನ್ ಸೇರಾವುನವು, ಅತ್ತಂದೆ ಪ್ರತ್ಯೇಕ ಲಿಂಕ್ ಲೆನ್ ಸೇರಾವುನವು — ಉಂದೆನ್ ಮಾತ್ರ ಆಧಾರ ಮಲ್ತ್ದ್ ಮಹತ್ವೊದ ಸುಧಾರಣೆಲೆನ್ ಪರಿಗಣಿಸರೆ ಆಪುಜಿ. == ಬಹುಮಾನೊಲು ಬೊಕ್ಕ ಗೌರವೊಲು == ಪ್ರಚಾರದ ನಿಯಮೊಲೆನ್ ಪಾಲನೆ ಮಲ್ಪುನ ಭಾಗವಹಿಸುವವೆರ್ ಈ ತಿರ್ತುದ ಗೌರವೊಲೆಗ್ ಅರ್ಹರಾದುಪ್ಪುವೆರ್: *ಮಾತಾ ಅರ್ಹ ಭಾಗವಹಿಸುನಕಲೆಗ್ ಡಿಜಿಟಲ್ ಪ್ರಮಾಣ ಪತ್ರೊಲು. *ಎಚ್ಚಿನ ಅಂಕೊಲೆನ್ ಪಡೆಯಿನ ಮೂಜಿ ಜನಕ್ಲೆಗ್ ಆನ್ ಲೈನ್ ಶಾಪಿಂಗ್ ವೌಚರ್ ಲು. ಭಾರತೀಯ ಬೊಕ್ಕ ಉಕ್ರೇನಿಯನ್ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ಶ್ರೇಣಿಲೆನ್ ಪರಿಗಣಿಸೊಡು: # ಸುರುತ ಸ್ಥಾನ — $100 / ₹10,000 #ರಡ್ಡನೆ ಸ್ಥಾನ — $50 / ₹5,000 #ಮೂಜಿನೇ ಸ್ಥಾನ — $30 / ₹3,000 ಅಭಿಯಾನದ ಫಲಿತಾಂಶೊಲೆನ್ ಪರಿಶೀಲನೆ ಮಲ್ತ್ ದ್ ಖಚಿತಪಡಿಸಾಯಿನ ಬುಕ್ಕೊ, ಸಂಘಟಕೆರ್ ಅರ್ಹ ಭಾಗವಹಿಸುವವರೆನ್ ಸಂಪರ್ಕ ಮಲ್ಪುವೆರ್. == ಸಂಘಟಕೆರ್ == ಹರಿಪ್ರಸಾದ್ ಶೆಟ್ಟಿ ([[User:Hariprasad Shetty10 |Hariprasad Shetty]]) == ಉಕ್ರೇನ್ ಕುರಿತಾಯಿನ ಲೇಕೊನೊದ ಪಟ್ಟಿ == https://meta.wikimedia.org/wiki/Event:Heritage_Across_Borders/Article_List ==ಇರೆನ ಲೇಕೊನೊಲೆನ್ ಮುಲ್ಪ ಸೇರಾಲೆ== https://wikieval.toolforge.org/contests/5 ==ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ== #[[ಬಳಕೆದಾರೆ:Hariprasad Shetty10|Hariprasad Shetty10]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Hariprasad Shetty10|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೬, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Santhosh Notagar99|Santhosh Notagar99]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Santhosh Notagar99|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೭, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೦೬:೦೩, ೧೫ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೩:೦೪, ೧೯ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Pragathi. BH|Pragathi. BH]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Pragathi. BH|ಪಾತೆರ್ಲೆ]]) ೨೧:೪೧, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Sannidhishetty|Sannidhishetty]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Sannidhishetty|ಪಾತೆರ್ಲೆ]]) ೧೩:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ಸಹಾಯೊ ಬೊಕ್ಕ ಪ್ರಶ್ನೆಲು == ಅಭಿಯಾನೊಗು ಸಂಬಂಧಪಟ್ಟಿನ ಸಾಮಾನ್ಯ ಪ್ರಶ್ನೆಲೆಗ್ ಅತ್ತಂದೆ ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್, ದಯಮಲ್ತ್ ಅಭಿಯಾನೊದ ಪಾತೆರೊದ ಪುಟೊನು ಬಳಕೆ ಮಲ್ಪುಲೆ. [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ]] |} 77ke9zv8uuycld5s4d0sy6zah3xmqzc 366170 366147 2026-08-21T09:40:03Z PrathikRamesh11 10089 /* ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ */ 366170 wikitext text/x-wiki '''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ'''<br> {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | [[File:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ.png|thumb|'''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ''']] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಲೆನ್ ಒಟ್ಟು ಮಲ್ಪುನ ಒಂಜಿ ಬಹುಭಾಷಿಕ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆದ ಅಭಿಯಾನ. ರಡ್ಡ್ ದೇಶೊಲೆನ ಸಮುದಾಯೊಲೆಡ್ಲಾ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಸಾಮಾನ್ಯ ಆಸಕ್ತಿ ಬೊಕ್ಕ ಮೆಚ್ಚುಗೆನ್ ಆಚರಣೆ ಮಲ್ಪುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು.<br> ಈ ಅಭಿಯಾನ '''15 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಭಾರತದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಡ್ ಸುರು ಆಪುಂಡು, ಬೊಕ್ಕ '''24 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಉಕ್ರೇನ್ ದೇಶದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಗ್ ಮುಗಿವುಂಡು. ಬೇತೆ ಬೇತೆ ಭಾಷೆಲೆನ ನಡುಟು ಜ್ಞಾನ ವಿನಿಮಯೊನು ಬುಲೆಪಾವುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು. ಉಕ್ರೇನ್ ದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಭಾರತೀಯ ಭಾಷೆದ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಂಚನೆ, ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಗು ಭಾರತೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾದ್ ಉಪ್ಪುನ ಬೇತೆ ಅರ್ಹ ವಿಷಯೊಲೆನ್ ಭಾಗವಹಿಸುನಕುಲು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. == ಪಾಲ್ ಪಡೆಯುನು ಎಂಚ == * ಪಾಲ್ ಪಡೆಯಿನಕುಲು ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ದ್ ಒಂಜಿ ವಿಷಯೊನು ಆಯ್ಕೆ ಮಲ್ಪೊಲಿ. * [https://meta.wikimedia.org/w/index.php?title=Special:EventDetails/4246&tab=WorklistPanel ಅಭಿಯಾನದ ಕೆಲಸೊದ ಪಟ್ಟಿನ್ ತೂಲೆ →] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ಡ್ ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ತಾಣೊಲೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಸೂಚಿತ ಲೇಖನೊಲೆನ್ ಈ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಉಂಡು. * ೧೫ ಬೊಕ್ಕ ೨೪ ಆಗಸ್ಟ್ ೨೦೨೬ ನಡುಟು ಒಂಜಿ ಲೇಖನೊನು ರಚನೆ ಮಲ್ಪುಲೆ ಅತ್ತುಡ ಸುಧಾರಿಸಾಲೆ. == ಅಂಕೊದ ವ್ಯವಸ್ಥೆ == ಅಭಿಯಾನದ ಅವಧಿಡ್ ಅರ್ಹ ಕೊಡುಗೆ ಕೊರ್ಪಿನ ಭಾಗವಹಿಸುವವರೆಗ್ ಅಂಕೊಲೆನ್ ಕೊರ್ಪೆರ್: *ಅರ್ಹತೆ ಪಡೆಯಿನ ಪೊಸ ಅತ್ತಂದೆ ಇತ್ತೆದ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಗು ಸೇರಾಯಿನ ಪ್ರತಿ 200 ಪದೊಕುಲೆಗ್ +1 ಅಂಕ. *ಗಣನೀಯವಾದ್ ಸುಧಾರಣೆ ಆಯಿನ ಪ್ರತಿಯೊಂಜಿ ಲೇಖನೊಗು +2 ಅಂಕೊಲು. ಗಮನಾರ್ಹ ಸುಧಾರಣೆ ಪಂಡ ಉಲ್ಲೇಕೊಲೆದೊಟ್ಟುಗು ಸಾಕಷ್ಟು ವಿಶ್ವಕೋಶದ ಮಾಹಿತಿನ್ ಒಳಗೊಂಡುದುಂಡು, ಲೇಖನದ ರಚನೆ ಅತ್ತಂದೆ ಓದರೆ ಸಾಮರ್ಥ್ಯನ್ ಗಮನಾರ್ಹವಾದ್ ಸುಧಾರಿಸವುಂಡು, ಗಮನಾರ್ಹವಾದ್ ಕಳೆದ್ ಪೋತಿನ ಮಾಹಿತಿನ್ ಸೇರಾವುಂಡು, ಅತ್ತಂದೆ ಅತೇ ಮಹತ್ವೊದ ಸುಧಾರಣೆನ್ ಮಲ್ಪುಂಡು. == ಅಂಕೊಲೆಗ್ ಅರ್ಹತೆ ಪಡೆಯೆರೆ == *ಒಂಜಿ ಪೊಸತ್ ರಚನೆ ಆಯಿನ ಲೇಖನೊಡು ಕನಿಷ್ಠ 500 ಪದೊಕುಲೆನ ಓದೆರೆ ಆಪಿನ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊಲು ಉಪ್ಪೊಡು. * ಒಂಜಿ ಇತ್ತೆದ ಲೇಖನೊಗು ಸುಧಾರಣೆ ಮಲ್ಪುನಗ ಕನಿಷ್ಠ 300 ಪದೊಕುಲೆನ ಪೊಸ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊನು ಸೇರಾವೊಡು. ಆಂಡಲಾ, ಸುಧಾರಣೆ ಮುಖ್ಯವಾದ್ ಮಹತ್ವದ ಪುನರ್ರಚನೆ, ಭಾಷಾ ಸಂಪಾದನೆ, ಮೂಲೊಲೆನ ಸೇರ್ಪಡೆ, ಅತ್ತಂದೆ ಬೇತೆ ಸ್ಪಷ್ಟವಾದ್ ಪ್ರದರ್ಶಿಸವೊಲಿಯಾಯಿನ ಮಹತ್ವದ ಸುಧಾರಣೆನ್ ಒಳಗೊಂಡಿತ್ತ್ಂಡ ಈ ನಿಯಮೊನು ಮನ್ನಾ ಮಲ್ಪೊಲಿ. *ಮಾತಾ ಅಗತ್ಯೊದ ಮುಖ್ಯ ಮಾಹಿತಿಲು ವಿಶ್ವಾಸಾರ್ಹ ಮೂಲೊಲೆನ್ ಆಧಾರವಾದ್ ಉಪ್ಪೊಡು. * ಮಾತ ಕೊಡುಗೆಲು ಸಮ್ಮಂದಪಟ್ಟಿನ ಬಾಸೆದ ವಿಕಿಪೀಡಿಯೊದ ನೀತಿ ಬೊಕ್ಕ ಮಾರ್ಗಸೂಚಿಲೆನ್ ಪಾಲನೆ ಮಲ್ಪೊಡು. *ಎಲ್ಯ/ಕಿನ್ಯ ಸಂಪಾದನೆಲು — ಉದಾಹರಣೆಗ್ ಮುದ್ರಣ ತಪ್ಪುಲೆನ್ ಸರಿ ಮಲ್ಪುನವು, ಲೇಔಟ್ ನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು, ವಿಭಾಗೊಲೆನ್ ಸೇರಾವುನವು, ಅತ್ತಂದೆ ಪ್ರತ್ಯೇಕ ಲಿಂಕ್ ಲೆನ್ ಸೇರಾವುನವು — ಉಂದೆನ್ ಮಾತ್ರ ಆಧಾರ ಮಲ್ತ್ದ್ ಮಹತ್ವೊದ ಸುಧಾರಣೆಲೆನ್ ಪರಿಗಣಿಸರೆ ಆಪುಜಿ. == ಬಹುಮಾನೊಲು ಬೊಕ್ಕ ಗೌರವೊಲು == ಪ್ರಚಾರದ ನಿಯಮೊಲೆನ್ ಪಾಲನೆ ಮಲ್ಪುನ ಭಾಗವಹಿಸುವವೆರ್ ಈ ತಿರ್ತುದ ಗೌರವೊಲೆಗ್ ಅರ್ಹರಾದುಪ್ಪುವೆರ್: *ಮಾತಾ ಅರ್ಹ ಭಾಗವಹಿಸುನಕಲೆಗ್ ಡಿಜಿಟಲ್ ಪ್ರಮಾಣ ಪತ್ರೊಲು. *ಎಚ್ಚಿನ ಅಂಕೊಲೆನ್ ಪಡೆಯಿನ ಮೂಜಿ ಜನಕ್ಲೆಗ್ ಆನ್ ಲೈನ್ ಶಾಪಿಂಗ್ ವೌಚರ್ ಲು. ಭಾರತೀಯ ಬೊಕ್ಕ ಉಕ್ರೇನಿಯನ್ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ಶ್ರೇಣಿಲೆನ್ ಪರಿಗಣಿಸೊಡು: # ಸುರುತ ಸ್ಥಾನ — $100 / ₹10,000 #ರಡ್ಡನೆ ಸ್ಥಾನ — $50 / ₹5,000 #ಮೂಜಿನೇ ಸ್ಥಾನ — $30 / ₹3,000 ಅಭಿಯಾನದ ಫಲಿತಾಂಶೊಲೆನ್ ಪರಿಶೀಲನೆ ಮಲ್ತ್ ದ್ ಖಚಿತಪಡಿಸಾಯಿನ ಬುಕ್ಕೊ, ಸಂಘಟಕೆರ್ ಅರ್ಹ ಭಾಗವಹಿಸುವವರೆನ್ ಸಂಪರ್ಕ ಮಲ್ಪುವೆರ್. == ಸಂಘಟಕೆರ್ == ಹರಿಪ್ರಸಾದ್ ಶೆಟ್ಟಿ ([[User:Hariprasad Shetty10 |Hariprasad Shetty]]) == ಉಕ್ರೇನ್ ಕುರಿತಾಯಿನ ಲೇಕೊನೊದ ಪಟ್ಟಿ == https://meta.wikimedia.org/wiki/Event:Heritage_Across_Borders/Article_List ==ಇರೆನ ಲೇಕೊನೊಲೆನ್ ಮುಲ್ಪ ಸೇರಾಲೆ== https://wikieval.toolforge.org/contests/5 ==ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ== #[[ಬಳಕೆದಾರೆ:Hariprasad Shetty10|Hariprasad Shetty10]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Hariprasad Shetty10|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೬, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Santhosh Notagar99|Santhosh Notagar99]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Santhosh Notagar99|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೭, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೦೬:೦೩, ೧೫ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೩:೦೪, ೧೯ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Pragathi. BH|Pragathi. BH]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Pragathi. BH|ಪಾತೆರ್ಲೆ]]) ೨೧:೪೧, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Sannidhishetty|Sannidhishetty]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Sannidhishetty|ಪಾತೆರ್ಲೆ]]) ೧೩:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:PrathikRamesh11|PrathikRamesh11]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:PrathikRamesh11|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ಸಹಾಯೊ ಬೊಕ್ಕ ಪ್ರಶ್ನೆಲು == ಅಭಿಯಾನೊಗು ಸಂಬಂಧಪಟ್ಟಿನ ಸಾಮಾನ್ಯ ಪ್ರಶ್ನೆಲೆಗ್ ಅತ್ತಂದೆ ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್, ದಯಮಲ್ತ್ ಅಭಿಯಾನೊದ ಪಾತೆರೊದ ಪುಟೊನು ಬಳಕೆ ಮಲ್ಪುಲೆ. [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ]] |} doynizll75rr0uhrbk9kghgmsw15dnc 366173 366170 2026-08-21T10:00:12Z Kavitha G. Kana 1040 /* ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ */ 366173 wikitext text/x-wiki '''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ'''<br> {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | [[File:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ.png|thumb|'''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ''']] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಲೆನ್ ಒಟ್ಟು ಮಲ್ಪುನ ಒಂಜಿ ಬಹುಭಾಷಿಕ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆದ ಅಭಿಯಾನ. ರಡ್ಡ್ ದೇಶೊಲೆನ ಸಮುದಾಯೊಲೆಡ್ಲಾ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಸಾಮಾನ್ಯ ಆಸಕ್ತಿ ಬೊಕ್ಕ ಮೆಚ್ಚುಗೆನ್ ಆಚರಣೆ ಮಲ್ಪುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು.<br> ಈ ಅಭಿಯಾನ '''15 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಭಾರತದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಡ್ ಸುರು ಆಪುಂಡು, ಬೊಕ್ಕ '''24 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಉಕ್ರೇನ್ ದೇಶದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಗ್ ಮುಗಿವುಂಡು. ಬೇತೆ ಬೇತೆ ಭಾಷೆಲೆನ ನಡುಟು ಜ್ಞಾನ ವಿನಿಮಯೊನು ಬುಲೆಪಾವುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು. ಉಕ್ರೇನ್ ದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಭಾರತೀಯ ಭಾಷೆದ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಂಚನೆ, ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಗು ಭಾರತೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾದ್ ಉಪ್ಪುನ ಬೇತೆ ಅರ್ಹ ವಿಷಯೊಲೆನ್ ಭಾಗವಹಿಸುನಕುಲು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. == ಪಾಲ್ ಪಡೆಯುನು ಎಂಚ == * ಪಾಲ್ ಪಡೆಯಿನಕುಲು ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ದ್ ಒಂಜಿ ವಿಷಯೊನು ಆಯ್ಕೆ ಮಲ್ಪೊಲಿ. * [https://meta.wikimedia.org/w/index.php?title=Special:EventDetails/4246&tab=WorklistPanel ಅಭಿಯಾನದ ಕೆಲಸೊದ ಪಟ್ಟಿನ್ ತೂಲೆ →] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ಡ್ ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ತಾಣೊಲೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಸೂಚಿತ ಲೇಖನೊಲೆನ್ ಈ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಉಂಡು. * ೧೫ ಬೊಕ್ಕ ೨೪ ಆಗಸ್ಟ್ ೨೦೨೬ ನಡುಟು ಒಂಜಿ ಲೇಖನೊನು ರಚನೆ ಮಲ್ಪುಲೆ ಅತ್ತುಡ ಸುಧಾರಿಸಾಲೆ. == ಅಂಕೊದ ವ್ಯವಸ್ಥೆ == ಅಭಿಯಾನದ ಅವಧಿಡ್ ಅರ್ಹ ಕೊಡುಗೆ ಕೊರ್ಪಿನ ಭಾಗವಹಿಸುವವರೆಗ್ ಅಂಕೊಲೆನ್ ಕೊರ್ಪೆರ್: *ಅರ್ಹತೆ ಪಡೆಯಿನ ಪೊಸ ಅತ್ತಂದೆ ಇತ್ತೆದ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಗು ಸೇರಾಯಿನ ಪ್ರತಿ 200 ಪದೊಕುಲೆಗ್ +1 ಅಂಕ. *ಗಣನೀಯವಾದ್ ಸುಧಾರಣೆ ಆಯಿನ ಪ್ರತಿಯೊಂಜಿ ಲೇಖನೊಗು +2 ಅಂಕೊಲು. ಗಮನಾರ್ಹ ಸುಧಾರಣೆ ಪಂಡ ಉಲ್ಲೇಕೊಲೆದೊಟ್ಟುಗು ಸಾಕಷ್ಟು ವಿಶ್ವಕೋಶದ ಮಾಹಿತಿನ್ ಒಳಗೊಂಡುದುಂಡು, ಲೇಖನದ ರಚನೆ ಅತ್ತಂದೆ ಓದರೆ ಸಾಮರ್ಥ್ಯನ್ ಗಮನಾರ್ಹವಾದ್ ಸುಧಾರಿಸವುಂಡು, ಗಮನಾರ್ಹವಾದ್ ಕಳೆದ್ ಪೋತಿನ ಮಾಹಿತಿನ್ ಸೇರಾವುಂಡು, ಅತ್ತಂದೆ ಅತೇ ಮಹತ್ವೊದ ಸುಧಾರಣೆನ್ ಮಲ್ಪುಂಡು. == ಅಂಕೊಲೆಗ್ ಅರ್ಹತೆ ಪಡೆಯೆರೆ == *ಒಂಜಿ ಪೊಸತ್ ರಚನೆ ಆಯಿನ ಲೇಖನೊಡು ಕನಿಷ್ಠ 500 ಪದೊಕುಲೆನ ಓದೆರೆ ಆಪಿನ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊಲು ಉಪ್ಪೊಡು. * ಒಂಜಿ ಇತ್ತೆದ ಲೇಖನೊಗು ಸುಧಾರಣೆ ಮಲ್ಪುನಗ ಕನಿಷ್ಠ 300 ಪದೊಕುಲೆನ ಪೊಸ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊನು ಸೇರಾವೊಡು. ಆಂಡಲಾ, ಸುಧಾರಣೆ ಮುಖ್ಯವಾದ್ ಮಹತ್ವದ ಪುನರ್ರಚನೆ, ಭಾಷಾ ಸಂಪಾದನೆ, ಮೂಲೊಲೆನ ಸೇರ್ಪಡೆ, ಅತ್ತಂದೆ ಬೇತೆ ಸ್ಪಷ್ಟವಾದ್ ಪ್ರದರ್ಶಿಸವೊಲಿಯಾಯಿನ ಮಹತ್ವದ ಸುಧಾರಣೆನ್ ಒಳಗೊಂಡಿತ್ತ್ಂಡ ಈ ನಿಯಮೊನು ಮನ್ನಾ ಮಲ್ಪೊಲಿ. *ಮಾತಾ ಅಗತ್ಯೊದ ಮುಖ್ಯ ಮಾಹಿತಿಲು ವಿಶ್ವಾಸಾರ್ಹ ಮೂಲೊಲೆನ್ ಆಧಾರವಾದ್ ಉಪ್ಪೊಡು. * ಮಾತ ಕೊಡುಗೆಲು ಸಮ್ಮಂದಪಟ್ಟಿನ ಬಾಸೆದ ವಿಕಿಪೀಡಿಯೊದ ನೀತಿ ಬೊಕ್ಕ ಮಾರ್ಗಸೂಚಿಲೆನ್ ಪಾಲನೆ ಮಲ್ಪೊಡು. *ಎಲ್ಯ/ಕಿನ್ಯ ಸಂಪಾದನೆಲು — ಉದಾಹರಣೆಗ್ ಮುದ್ರಣ ತಪ್ಪುಲೆನ್ ಸರಿ ಮಲ್ಪುನವು, ಲೇಔಟ್ ನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು, ವಿಭಾಗೊಲೆನ್ ಸೇರಾವುನವು, ಅತ್ತಂದೆ ಪ್ರತ್ಯೇಕ ಲಿಂಕ್ ಲೆನ್ ಸೇರಾವುನವು — ಉಂದೆನ್ ಮಾತ್ರ ಆಧಾರ ಮಲ್ತ್ದ್ ಮಹತ್ವೊದ ಸುಧಾರಣೆಲೆನ್ ಪರಿಗಣಿಸರೆ ಆಪುಜಿ. == ಬಹುಮಾನೊಲು ಬೊಕ್ಕ ಗೌರವೊಲು == ಪ್ರಚಾರದ ನಿಯಮೊಲೆನ್ ಪಾಲನೆ ಮಲ್ಪುನ ಭಾಗವಹಿಸುವವೆರ್ ಈ ತಿರ್ತುದ ಗೌರವೊಲೆಗ್ ಅರ್ಹರಾದುಪ್ಪುವೆರ್: *ಮಾತಾ ಅರ್ಹ ಭಾಗವಹಿಸುನಕಲೆಗ್ ಡಿಜಿಟಲ್ ಪ್ರಮಾಣ ಪತ್ರೊಲು. *ಎಚ್ಚಿನ ಅಂಕೊಲೆನ್ ಪಡೆಯಿನ ಮೂಜಿ ಜನಕ್ಲೆಗ್ ಆನ್ ಲೈನ್ ಶಾಪಿಂಗ್ ವೌಚರ್ ಲು. ಭಾರತೀಯ ಬೊಕ್ಕ ಉಕ್ರೇನಿಯನ್ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ಶ್ರೇಣಿಲೆನ್ ಪರಿಗಣಿಸೊಡು: # ಸುರುತ ಸ್ಥಾನ — $100 / ₹10,000 #ರಡ್ಡನೆ ಸ್ಥಾನ — $50 / ₹5,000 #ಮೂಜಿನೇ ಸ್ಥಾನ — $30 / ₹3,000 ಅಭಿಯಾನದ ಫಲಿತಾಂಶೊಲೆನ್ ಪರಿಶೀಲನೆ ಮಲ್ತ್ ದ್ ಖಚಿತಪಡಿಸಾಯಿನ ಬುಕ್ಕೊ, ಸಂಘಟಕೆರ್ ಅರ್ಹ ಭಾಗವಹಿಸುವವರೆನ್ ಸಂಪರ್ಕ ಮಲ್ಪುವೆರ್. == ಸಂಘಟಕೆರ್ == ಹರಿಪ್ರಸಾದ್ ಶೆಟ್ಟಿ ([[User:Hariprasad Shetty10 |Hariprasad Shetty]]) == ಉಕ್ರೇನ್ ಕುರಿತಾಯಿನ ಲೇಕೊನೊದ ಪಟ್ಟಿ == https://meta.wikimedia.org/wiki/Event:Heritage_Across_Borders/Article_List ==ಇರೆನ ಲೇಕೊನೊಲೆನ್ ಮುಲ್ಪ ಸೇರಾಲೆ== https://wikieval.toolforge.org/contests/5 ==ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ== #[[ಬಳಕೆದಾರೆ:Hariprasad Shetty10|Hariprasad Shetty10]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Hariprasad Shetty10|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೬, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Santhosh Notagar99|Santhosh Notagar99]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Santhosh Notagar99|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೭, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೦೬:೦೩, ೧೫ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೩:೦೪, ೧೯ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Pragathi. BH|Pragathi. BH]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Pragathi. BH|ಪಾತೆರ್ಲೆ]]) ೨೧:೪೧, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Sannidhishetty|Sannidhishetty]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Sannidhishetty|ಪಾತೆರ್ಲೆ]]) ೧೩:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:PrathikRamesh11|PrathikRamesh11]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:PrathikRamesh11|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) --[[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೫:೩೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ಸಹಾಯೊ ಬೊಕ್ಕ ಪ್ರಶ್ನೆಲು == ಅಭಿಯಾನೊಗು ಸಂಬಂಧಪಟ್ಟಿನ ಸಾಮಾನ್ಯ ಪ್ರಶ್ನೆಲೆಗ್ ಅತ್ತಂದೆ ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್, ದಯಮಲ್ತ್ ಅಭಿಯಾನೊದ ಪಾತೆರೊದ ಪುಟೊನು ಬಳಕೆ ಮಲ್ಪುಲೆ. [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ]] |} 6pcf7u5auaqnp81ruhq8av274qcsdxn 366174 366173 2026-08-21T10:00:37Z Kavitha G. Kana 1040 /* ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ */ 366174 wikitext text/x-wiki '''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ'''<br> {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | [[File:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ.png|thumb|'''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ''']] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಲೆನ್ ಒಟ್ಟು ಮಲ್ಪುನ ಒಂಜಿ ಬಹುಭಾಷಿಕ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆದ ಅಭಿಯಾನ. ರಡ್ಡ್ ದೇಶೊಲೆನ ಸಮುದಾಯೊಲೆಡ್ಲಾ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಸಾಮಾನ್ಯ ಆಸಕ್ತಿ ಬೊಕ್ಕ ಮೆಚ್ಚುಗೆನ್ ಆಚರಣೆ ಮಲ್ಪುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು.<br> ಈ ಅಭಿಯಾನ '''15 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಭಾರತದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಡ್ ಸುರು ಆಪುಂಡು, ಬೊಕ್ಕ '''24 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಉಕ್ರೇನ್ ದೇಶದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಗ್ ಮುಗಿವುಂಡು. ಬೇತೆ ಬೇತೆ ಭಾಷೆಲೆನ ನಡುಟು ಜ್ಞಾನ ವಿನಿಮಯೊನು ಬುಲೆಪಾವುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು. ಉಕ್ರೇನ್ ದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಭಾರತೀಯ ಭಾಷೆದ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಂಚನೆ, ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಗು ಭಾರತೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾದ್ ಉಪ್ಪುನ ಬೇತೆ ಅರ್ಹ ವಿಷಯೊಲೆನ್ ಭಾಗವಹಿಸುನಕುಲು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. == ಪಾಲ್ ಪಡೆಯುನು ಎಂಚ == * ಪಾಲ್ ಪಡೆಯಿನಕುಲು ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ದ್ ಒಂಜಿ ವಿಷಯೊನು ಆಯ್ಕೆ ಮಲ್ಪೊಲಿ. * [https://meta.wikimedia.org/w/index.php?title=Special:EventDetails/4246&tab=WorklistPanel ಅಭಿಯಾನದ ಕೆಲಸೊದ ಪಟ್ಟಿನ್ ತೂಲೆ →] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ಡ್ ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ತಾಣೊಲೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಸೂಚಿತ ಲೇಖನೊಲೆನ್ ಈ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಉಂಡು. * ೧೫ ಬೊಕ್ಕ ೨೪ ಆಗಸ್ಟ್ ೨೦೨೬ ನಡುಟು ಒಂಜಿ ಲೇಖನೊನು ರಚನೆ ಮಲ್ಪುಲೆ ಅತ್ತುಡ ಸುಧಾರಿಸಾಲೆ. == ಅಂಕೊದ ವ್ಯವಸ್ಥೆ == ಅಭಿಯಾನದ ಅವಧಿಡ್ ಅರ್ಹ ಕೊಡುಗೆ ಕೊರ್ಪಿನ ಭಾಗವಹಿಸುವವರೆಗ್ ಅಂಕೊಲೆನ್ ಕೊರ್ಪೆರ್: *ಅರ್ಹತೆ ಪಡೆಯಿನ ಪೊಸ ಅತ್ತಂದೆ ಇತ್ತೆದ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಗು ಸೇರಾಯಿನ ಪ್ರತಿ 200 ಪದೊಕುಲೆಗ್ +1 ಅಂಕ. *ಗಣನೀಯವಾದ್ ಸುಧಾರಣೆ ಆಯಿನ ಪ್ರತಿಯೊಂಜಿ ಲೇಖನೊಗು +2 ಅಂಕೊಲು. ಗಮನಾರ್ಹ ಸುಧಾರಣೆ ಪಂಡ ಉಲ್ಲೇಕೊಲೆದೊಟ್ಟುಗು ಸಾಕಷ್ಟು ವಿಶ್ವಕೋಶದ ಮಾಹಿತಿನ್ ಒಳಗೊಂಡುದುಂಡು, ಲೇಖನದ ರಚನೆ ಅತ್ತಂದೆ ಓದರೆ ಸಾಮರ್ಥ್ಯನ್ ಗಮನಾರ್ಹವಾದ್ ಸುಧಾರಿಸವುಂಡು, ಗಮನಾರ್ಹವಾದ್ ಕಳೆದ್ ಪೋತಿನ ಮಾಹಿತಿನ್ ಸೇರಾವುಂಡು, ಅತ್ತಂದೆ ಅತೇ ಮಹತ್ವೊದ ಸುಧಾರಣೆನ್ ಮಲ್ಪುಂಡು. == ಅಂಕೊಲೆಗ್ ಅರ್ಹತೆ ಪಡೆಯೆರೆ == *ಒಂಜಿ ಪೊಸತ್ ರಚನೆ ಆಯಿನ ಲೇಖನೊಡು ಕನಿಷ್ಠ 500 ಪದೊಕುಲೆನ ಓದೆರೆ ಆಪಿನ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊಲು ಉಪ್ಪೊಡು. * ಒಂಜಿ ಇತ್ತೆದ ಲೇಖನೊಗು ಸುಧಾರಣೆ ಮಲ್ಪುನಗ ಕನಿಷ್ಠ 300 ಪದೊಕುಲೆನ ಪೊಸ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊನು ಸೇರಾವೊಡು. ಆಂಡಲಾ, ಸುಧಾರಣೆ ಮುಖ್ಯವಾದ್ ಮಹತ್ವದ ಪುನರ್ರಚನೆ, ಭಾಷಾ ಸಂಪಾದನೆ, ಮೂಲೊಲೆನ ಸೇರ್ಪಡೆ, ಅತ್ತಂದೆ ಬೇತೆ ಸ್ಪಷ್ಟವಾದ್ ಪ್ರದರ್ಶಿಸವೊಲಿಯಾಯಿನ ಮಹತ್ವದ ಸುಧಾರಣೆನ್ ಒಳಗೊಂಡಿತ್ತ್ಂಡ ಈ ನಿಯಮೊನು ಮನ್ನಾ ಮಲ್ಪೊಲಿ. *ಮಾತಾ ಅಗತ್ಯೊದ ಮುಖ್ಯ ಮಾಹಿತಿಲು ವಿಶ್ವಾಸಾರ್ಹ ಮೂಲೊಲೆನ್ ಆಧಾರವಾದ್ ಉಪ್ಪೊಡು. * ಮಾತ ಕೊಡುಗೆಲು ಸಮ್ಮಂದಪಟ್ಟಿನ ಬಾಸೆದ ವಿಕಿಪೀಡಿಯೊದ ನೀತಿ ಬೊಕ್ಕ ಮಾರ್ಗಸೂಚಿಲೆನ್ ಪಾಲನೆ ಮಲ್ಪೊಡು. *ಎಲ್ಯ/ಕಿನ್ಯ ಸಂಪಾದನೆಲು — ಉದಾಹರಣೆಗ್ ಮುದ್ರಣ ತಪ್ಪುಲೆನ್ ಸರಿ ಮಲ್ಪುನವು, ಲೇಔಟ್ ನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು, ವಿಭಾಗೊಲೆನ್ ಸೇರಾವುನವು, ಅತ್ತಂದೆ ಪ್ರತ್ಯೇಕ ಲಿಂಕ್ ಲೆನ್ ಸೇರಾವುನವು — ಉಂದೆನ್ ಮಾತ್ರ ಆಧಾರ ಮಲ್ತ್ದ್ ಮಹತ್ವೊದ ಸುಧಾರಣೆಲೆನ್ ಪರಿಗಣಿಸರೆ ಆಪುಜಿ. == ಬಹುಮಾನೊಲು ಬೊಕ್ಕ ಗೌರವೊಲು == ಪ್ರಚಾರದ ನಿಯಮೊಲೆನ್ ಪಾಲನೆ ಮಲ್ಪುನ ಭಾಗವಹಿಸುವವೆರ್ ಈ ತಿರ್ತುದ ಗೌರವೊಲೆಗ್ ಅರ್ಹರಾದುಪ್ಪುವೆರ್: *ಮಾತಾ ಅರ್ಹ ಭಾಗವಹಿಸುನಕಲೆಗ್ ಡಿಜಿಟಲ್ ಪ್ರಮಾಣ ಪತ್ರೊಲು. *ಎಚ್ಚಿನ ಅಂಕೊಲೆನ್ ಪಡೆಯಿನ ಮೂಜಿ ಜನಕ್ಲೆಗ್ ಆನ್ ಲೈನ್ ಶಾಪಿಂಗ್ ವೌಚರ್ ಲು. ಭಾರತೀಯ ಬೊಕ್ಕ ಉಕ್ರೇನಿಯನ್ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ಶ್ರೇಣಿಲೆನ್ ಪರಿಗಣಿಸೊಡು: # ಸುರುತ ಸ್ಥಾನ — $100 / ₹10,000 #ರಡ್ಡನೆ ಸ್ಥಾನ — $50 / ₹5,000 #ಮೂಜಿನೇ ಸ್ಥಾನ — $30 / ₹3,000 ಅಭಿಯಾನದ ಫಲಿತಾಂಶೊಲೆನ್ ಪರಿಶೀಲನೆ ಮಲ್ತ್ ದ್ ಖಚಿತಪಡಿಸಾಯಿನ ಬುಕ್ಕೊ, ಸಂಘಟಕೆರ್ ಅರ್ಹ ಭಾಗವಹಿಸುವವರೆನ್ ಸಂಪರ್ಕ ಮಲ್ಪುವೆರ್. == ಸಂಘಟಕೆರ್ == ಹರಿಪ್ರಸಾದ್ ಶೆಟ್ಟಿ ([[User:Hariprasad Shetty10 |Hariprasad Shetty]]) == ಉಕ್ರೇನ್ ಕುರಿತಾಯಿನ ಲೇಕೊನೊದ ಪಟ್ಟಿ == https://meta.wikimedia.org/wiki/Event:Heritage_Across_Borders/Article_List ==ಇರೆನ ಲೇಕೊನೊಲೆನ್ ಮುಲ್ಪ ಸೇರಾಲೆ== https://wikieval.toolforge.org/contests/5 ==ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ== #[[ಬಳಕೆದಾರೆ:Hariprasad Shetty10|Hariprasad Shetty10]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Hariprasad Shetty10|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೬, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Santhosh Notagar99|Santhosh Notagar99]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Santhosh Notagar99|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೭, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೦೬:೦೩, ೧೫ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೩:೦೪, ೧೯ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Pragathi. BH|Pragathi. BH]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Pragathi. BH|ಪಾತೆರ್ಲೆ]]) ೨೧:೪೧, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Sannidhishetty|Sannidhishetty]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Sannidhishetty|ಪಾತೆರ್ಲೆ]]) ೧೩:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:PrathikRamesh11|PrathikRamesh11]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:PrathikRamesh11|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೫:೩೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ಸಹಾಯೊ ಬೊಕ್ಕ ಪ್ರಶ್ನೆಲು == ಅಭಿಯಾನೊಗು ಸಂಬಂಧಪಟ್ಟಿನ ಸಾಮಾನ್ಯ ಪ್ರಶ್ನೆಲೆಗ್ ಅತ್ತಂದೆ ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್, ದಯಮಲ್ತ್ ಅಭಿಯಾನೊದ ಪಾತೆರೊದ ಪುಟೊನು ಬಳಕೆ ಮಲ್ಪುಲೆ. [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ]] |} nnld25gwuoboozcq9iywg27649voqqs 366177 366174 2026-08-21T10:05:37Z Ashwini Rai K 9755 /* ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ */ 366177 wikitext text/x-wiki '''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ'''<br> {| style="border: 2px solid #FFCC00; background-color: #FFF9E6; padding: 10px; border-radius: 8px; font-family: sans-serif; width: 100%;" |- | [[File:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ.png|thumb|'''ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ''']] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಲೆನ್ ಒಟ್ಟು ಮಲ್ಪುನ ಒಂಜಿ ಬಹುಭಾಷಿಕ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆದ ಅಭಿಯಾನ. ರಡ್ಡ್ ದೇಶೊಲೆನ ಸಮುದಾಯೊಲೆಡ್ಲಾ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಸಾಮಾನ್ಯ ಆಸಕ್ತಿ ಬೊಕ್ಕ ಮೆಚ್ಚುಗೆನ್ ಆಚರಣೆ ಮಲ್ಪುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು.<br> ಈ ಅಭಿಯಾನ '''15 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಭಾರತದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಡ್ ಸುರು ಆಪುಂಡು, ಬೊಕ್ಕ '''24 ಆಗಸ್ಟ್ 2026''' ಗ್, '''ಉಕ್ರೇನ್ ದೇಶದ ಸ್ವಾತಂತ್ರ್ಯ ದಿನಾಚರಣೆ'''ಗ್ ಮುಗಿವುಂಡು. ಬೇತೆ ಬೇತೆ ಭಾಷೆಲೆನ ನಡುಟು ಜ್ಞಾನ ವಿನಿಮಯೊನು ಬುಲೆಪಾವುನವು ಈ ಅಭಿಯಾನೊದ ಮುಖ್ಯ ಉದ್ದೇಶ ಆದುಂಡು. ಉಕ್ರೇನ್ ದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಭಾರತೀಯ ಭಾಷೆದ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಂಚನೆ, ಉಕ್ರೇನ್ ದ ವಿಕಿಮೀಡಿಯಾ ಸಮುದಾಯೊಗು ಭಾರತೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆದ ಬಗ್ಗೆ ಲೇಖನೊಲೆನ್ ರಚನೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ಸುಧಾರಣೆ ಮಲ್ಪೆರೆ ಪ್ರೋತ್ಸಾಹ ಕೊರ್ಪೆರ್. ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾದ್ ಉಪ್ಪುನ ಬೇತೆ ಅರ್ಹ ವಿಷಯೊಲೆನ್ ಭಾಗವಹಿಸುನಕುಲು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. == ಪಾಲ್ ಪಡೆಯುನು ಎಂಚ == * ಪಾಲ್ ಪಡೆಯಿನಕುಲು ಅಭಿಯಾನದ ಕೆಲಸದ ಪಟ್ಟಿಡ್ದ್ ಒಂಜಿ ವಿಷಯೊನು ಆಯ್ಕೆ ಮಲ್ಪೊಲಿ. * [https://meta.wikimedia.org/w/index.php?title=Special:EventDetails/4246&tab=WorklistPanel ಅಭಿಯಾನದ ಕೆಲಸೊದ ಪಟ್ಟಿನ್ ತೂಲೆ →] ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ಡ್ ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ತಾಣೊಲೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಸೂಚಿತ ಲೇಖನೊಲೆನ್ ಈ ಕೆಲಸದ ಪಟ್ಟಿಡ್ ಉಂಡು. * ೧೫ ಬೊಕ್ಕ ೨೪ ಆಗಸ್ಟ್ ೨೦೨೬ ನಡುಟು ಒಂಜಿ ಲೇಖನೊನು ರಚನೆ ಮಲ್ಪುಲೆ ಅತ್ತುಡ ಸುಧಾರಿಸಾಲೆ. == ಅಂಕೊದ ವ್ಯವಸ್ಥೆ == ಅಭಿಯಾನದ ಅವಧಿಡ್ ಅರ್ಹ ಕೊಡುಗೆ ಕೊರ್ಪಿನ ಭಾಗವಹಿಸುವವರೆಗ್ ಅಂಕೊಲೆನ್ ಕೊರ್ಪೆರ್: *ಅರ್ಹತೆ ಪಡೆಯಿನ ಪೊಸ ಅತ್ತಂದೆ ಇತ್ತೆದ ವಿಕಿಪೀಡಿಯ ಲೇಖನೊಗು ಸೇರಾಯಿನ ಪ್ರತಿ 200 ಪದೊಕುಲೆಗ್ +1 ಅಂಕ. *ಗಣನೀಯವಾದ್ ಸುಧಾರಣೆ ಆಯಿನ ಪ್ರತಿಯೊಂಜಿ ಲೇಖನೊಗು +2 ಅಂಕೊಲು. ಗಮನಾರ್ಹ ಸುಧಾರಣೆ ಪಂಡ ಉಲ್ಲೇಕೊಲೆದೊಟ್ಟುಗು ಸಾಕಷ್ಟು ವಿಶ್ವಕೋಶದ ಮಾಹಿತಿನ್ ಒಳಗೊಂಡುದುಂಡು, ಲೇಖನದ ರಚನೆ ಅತ್ತಂದೆ ಓದರೆ ಸಾಮರ್ಥ್ಯನ್ ಗಮನಾರ್ಹವಾದ್ ಸುಧಾರಿಸವುಂಡು, ಗಮನಾರ್ಹವಾದ್ ಕಳೆದ್ ಪೋತಿನ ಮಾಹಿತಿನ್ ಸೇರಾವುಂಡು, ಅತ್ತಂದೆ ಅತೇ ಮಹತ್ವೊದ ಸುಧಾರಣೆನ್ ಮಲ್ಪುಂಡು. == ಅಂಕೊಲೆಗ್ ಅರ್ಹತೆ ಪಡೆಯೆರೆ == *ಒಂಜಿ ಪೊಸತ್ ರಚನೆ ಆಯಿನ ಲೇಖನೊಡು ಕನಿಷ್ಠ 500 ಪದೊಕುಲೆನ ಓದೆರೆ ಆಪಿನ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊಲು ಉಪ್ಪೊಡು. * ಒಂಜಿ ಇತ್ತೆದ ಲೇಖನೊಗು ಸುಧಾರಣೆ ಮಲ್ಪುನಗ ಕನಿಷ್ಠ 300 ಪದೊಕುಲೆನ ಪೊಸ ಬೊಕ್ಕ ಅರ್ಥಪೂರ್ಣ ವಿಷಯೊನು ಸೇರಾವೊಡು. ಆಂಡಲಾ, ಸುಧಾರಣೆ ಮುಖ್ಯವಾದ್ ಮಹತ್ವದ ಪುನರ್ರಚನೆ, ಭಾಷಾ ಸಂಪಾದನೆ, ಮೂಲೊಲೆನ ಸೇರ್ಪಡೆ, ಅತ್ತಂದೆ ಬೇತೆ ಸ್ಪಷ್ಟವಾದ್ ಪ್ರದರ್ಶಿಸವೊಲಿಯಾಯಿನ ಮಹತ್ವದ ಸುಧಾರಣೆನ್ ಒಳಗೊಂಡಿತ್ತ್ಂಡ ಈ ನಿಯಮೊನು ಮನ್ನಾ ಮಲ್ಪೊಲಿ. *ಮಾತಾ ಅಗತ್ಯೊದ ಮುಖ್ಯ ಮಾಹಿತಿಲು ವಿಶ್ವಾಸಾರ್ಹ ಮೂಲೊಲೆನ್ ಆಧಾರವಾದ್ ಉಪ್ಪೊಡು. * ಮಾತ ಕೊಡುಗೆಲು ಸಮ್ಮಂದಪಟ್ಟಿನ ಬಾಸೆದ ವಿಕಿಪೀಡಿಯೊದ ನೀತಿ ಬೊಕ್ಕ ಮಾರ್ಗಸೂಚಿಲೆನ್ ಪಾಲನೆ ಮಲ್ಪೊಡು. *ಎಲ್ಯ/ಕಿನ್ಯ ಸಂಪಾದನೆಲು — ಉದಾಹರಣೆಗ್ ಮುದ್ರಣ ತಪ್ಪುಲೆನ್ ಸರಿ ಮಲ್ಪುನವು, ಲೇಔಟ್ ನ್ ಬದಲಾವಣೆ ಮಲ್ಪುನವು, ವಿಭಾಗೊಲೆನ್ ಸೇರಾವುನವು, ಅತ್ತಂದೆ ಪ್ರತ್ಯೇಕ ಲಿಂಕ್ ಲೆನ್ ಸೇರಾವುನವು — ಉಂದೆನ್ ಮಾತ್ರ ಆಧಾರ ಮಲ್ತ್ದ್ ಮಹತ್ವೊದ ಸುಧಾರಣೆಲೆನ್ ಪರಿಗಣಿಸರೆ ಆಪುಜಿ. == ಬಹುಮಾನೊಲು ಬೊಕ್ಕ ಗೌರವೊಲು == ಪ್ರಚಾರದ ನಿಯಮೊಲೆನ್ ಪಾಲನೆ ಮಲ್ಪುನ ಭಾಗವಹಿಸುವವೆರ್ ಈ ತಿರ್ತುದ ಗೌರವೊಲೆಗ್ ಅರ್ಹರಾದುಪ್ಪುವೆರ್: *ಮಾತಾ ಅರ್ಹ ಭಾಗವಹಿಸುನಕಲೆಗ್ ಡಿಜಿಟಲ್ ಪ್ರಮಾಣ ಪತ್ರೊಲು. *ಎಚ್ಚಿನ ಅಂಕೊಲೆನ್ ಪಡೆಯಿನ ಮೂಜಿ ಜನಕ್ಲೆಗ್ ಆನ್ ಲೈನ್ ಶಾಪಿಂಗ್ ವೌಚರ್ ಲು. ಭಾರತೀಯ ಬೊಕ್ಕ ಉಕ್ರೇನಿಯನ್ ಸಮುದಾಯೊಲೆಗ್ ಪ್ರತ್ಯೇಕ ಶ್ರೇಣಿಲೆನ್ ಪರಿಗಣಿಸೊಡು: # ಸುರುತ ಸ್ಥಾನ — $100 / ₹10,000 #ರಡ್ಡನೆ ಸ್ಥಾನ — $50 / ₹5,000 #ಮೂಜಿನೇ ಸ್ಥಾನ — $30 / ₹3,000 ಅಭಿಯಾನದ ಫಲಿತಾಂಶೊಲೆನ್ ಪರಿಶೀಲನೆ ಮಲ್ತ್ ದ್ ಖಚಿತಪಡಿಸಾಯಿನ ಬುಕ್ಕೊ, ಸಂಘಟಕೆರ್ ಅರ್ಹ ಭಾಗವಹಿಸುವವರೆನ್ ಸಂಪರ್ಕ ಮಲ್ಪುವೆರ್. == ಸಂಘಟಕೆರ್ == ಹರಿಪ್ರಸಾದ್ ಶೆಟ್ಟಿ ([[User:Hariprasad Shetty10 |Hariprasad Shetty]]) == ಉಕ್ರೇನ್ ಕುರಿತಾಯಿನ ಲೇಕೊನೊದ ಪಟ್ಟಿ == https://meta.wikimedia.org/wiki/Event:Heritage_Across_Borders/Article_List ==ಇರೆನ ಲೇಕೊನೊಲೆನ್ ಮುಲ್ಪ ಸೇರಾಲೆ== https://wikieval.toolforge.org/contests/5 ==ಪಾಲು ದೆತ್ತೊನುನಕುಲು ಮುಲ್ಪ ಸಹಿ ಮನ್ಪುಲೆ== #[[ಬಳಕೆದಾರೆ:Hariprasad Shetty10|Hariprasad Shetty10]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Hariprasad Shetty10|ಪಾತೆರ್ಲೆ]]) ೧೪:೩೬, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Santhosh Notagar99|Santhosh Notagar99]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Santhosh Notagar99|ಪಾತೆರ್ಲೆ]]) ೧೪:೫೭, ೧೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) # [[ಬಳಕೆದಾರೆ:Mahaveer Indra|Mahaveer Indra]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Mahaveer Indra|ಪಾತೆರ್ಲೆ]]) ೦೬:೦೩, ೧೫ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Shreelatha.Halemane|Shreelatha.Halemane]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Shreelatha.Halemane|ಪಾತೆರ್ಲೆ]]) ೧೩:೦೪, ೧೯ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Pragathi. BH|Pragathi. BH]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Pragathi. BH|ಪಾತೆರ್ಲೆ]]) ೨೧:೪೧, ೨೦ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Sannidhishetty|Sannidhishetty]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Sannidhishetty|ಪಾತೆರ್ಲೆ]]) ೧೩:೧೮, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:PrathikRamesh11|PrathikRamesh11]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:PrathikRamesh11|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #--[[ಬಳಕೆದಾರೆ:Kavitha G. Kana|Kavitha G. Kana]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Kavitha G. Kana|ಪಾತೆರ್ಲೆ]]) ೧೫:೩೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) #----[[ಬಳಕೆದಾರೆ:Ashwini Rai K|Ashwini Rai K]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:Ashwini Rai K|ಪಾತೆರ್ಲೆ]]) ೧೫:೩೫, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) == ಸಹಾಯೊ ಬೊಕ್ಕ ಪ್ರಶ್ನೆಲು == ಅಭಿಯಾನೊಗು ಸಂಬಂಧಪಟ್ಟಿನ ಸಾಮಾನ್ಯ ಪ್ರಶ್ನೆಲೆಗ್ ಅತ್ತಂದೆ ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್, ದಯಮಲ್ತ್ ಅಭಿಯಾನೊದ ಪಾತೆರೊದ ಪುಟೊನು ಬಳಕೆ ಮಲ್ಪುಲೆ. [[ವರ್ಗೊ:ವಿಕಿಪೀಡಿಯ:ಗಡಿದಂಚಿದ ಪರಂಪರೆ - ಭಾರತ ಬೊಕ್ಕ ಉಕ್ರೇನ್ ದ ವಿಕಿಪೀಡಿಯ ಸಂಪಾದನೆ ಅಭಿಯಾನ]] |} 97akf06n47iflz2caaoy00ygkngzy80 ಅಡಮಿಂತೆ ಮಗನ್ ಅಬು 0 27868 366093 365674 2026-08-20T23:43:07Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366093 wikitext text/x-wiki {{Short description|2011 film by Salim Ahamed}} {{Use dmy dates|date=April 2019}} {{Infobox film | name = ಆದಮಿಂಟೆ ಮಗನ್ ಅಬು | image = AdaminteMakanAbu.JPG | caption = Theatrical release poster | director = ಸಲೀಂ ಅಹಮದ್ | producer = ಸಲೀಂ ಅಹಮದ್ | writer = ಸಲೀಂ ಅಹಮದ್ | screenplay = | story = | based_on = <!-- {{based on|title of the original work|writer of the original work}} --> | starring = ಸಲೀಂ ಕುಮಾರ್<br />ಜರೀನಾ ವಹಾಬ್ | music = ರಮೇಶ್ ನಾರಾಯಣ್ ಐಸಾಕ್ ಥಾಮಸ್ ಕೊಟ್ಟುಕಪಲ್ಲಿ | cinematography = ಮಧು ಅಂಬಾಟ್ | editing = ವಿಜಯ್ ಶಂಕರ್ | studio = ಅಲೆನ್ಸ್ ಮೀಡಿಯಾ | distributor = ಕಲಾಸಂಘಂ ಫಿಲ್ಮ್ಸ್ | released = {{ 24 June 2011<ref>{{cite news|url=http://www.mathrubhumi.com/movies/malayalam/194880/ |title=ആദാമിന്റെ മകന്‍ അബു 24ന് |language=ml |date=21 June 2011 |work=[[Mathrubhumi]] |access-date=24 June 2011 |url-status=dead |archive-url=https://web.archive.org/web/20110625073205/http://www.mathrubhumi.com/movies/malayalam/194880/ |archive-date=25 June 2011 }}</ref>}} | runtime = 101 ನಿಮಿಷಗಳು | country = ಭಾರತ | language = ಮಲಯಾಳಂ | budget = {{INR}}1.5 [[crore]]<ref name="Distribution">{{cite news|url=http://www.deccanchronicle.com/channels/showbiz/others/small-budget-adaminte-makan-set-big-release-017 |author=Sreenivasa Raghavan |title=Small-budget Adaminte Makan... set for big release |date=21 May 2011 |work=Deccan Chronicle |location=India |access-date=25 May 2011 |url-status=dead |archive-url=https://web.archive.org/web/20110524045253/http://www.deccanchronicle.com/channels/showbiz/others/small-budget-adaminte-makan-set-big-release-017 |archive-date=24 May 2011 }}</ref> | gross = }} '''ಅಡಮಿಂತೆ ಮಗನ್ ಅಬು''' (ಅನುವಾದ: ಅಬು, ಆಡಮ್ ನ ಮಗೆ) 2011 ರ ಭಾರತೀಯ ಮಲಯಾಳಂ ಭಾಷೆದ ಸಾಮಾಜಿಕ ನಾಟಕ ಚಿತ್ರ. ಈ ಸಿನಿಮಾದ ಬರವು, ನಿರ್ದೇಶನ ಬೊಕ್ಕ ಸಹ ನಿರ್ಮಾಪಕೆರ್ ಸಲೀಮ್ ಅಹ್ಮದ್. ಸಲೀಂ ಕುಮಾರ್ ಬೊಕ್ಕ ಜರೀನಾ ವಹಾಬ್ ಮುಖ್ಯ ಪಾತ್ರೊಡು, ಮುಕೇಶ್, ಕಾಲಭವನ ಮಾಣಿ, ಸೂರಜ್ ವೆಂಜರಾಮೂಡು ಬೊಕ್ಕ ನೆಡುಮುಡಿ ವೇಣು ಪೋಷಕ ಪಾತ್ರೊಡು ನಟನೆ ಮಲ್ತೊಂದುಲ್ಲೆರ್. ಛಾಯಾಗ್ರಹಣ ಮಧು ಅಂಬತ್ ಮಲ್ತೆರ್. ಪಾಡ್ದನೊಲೆನ್ ರಚನೆ ಮಲ್ತೆರ್ ರಮೇಶ್ ನಾರಾಯಣೆರ್ ಬೊಕ್ಕ ಹಿನ್ನೆಲೆ ಸಂಗೀತೊನು ಐಸಾಕ್ ಥಾಮಸ್ ಕೊಟ್ಟುಪಳ್ಳಿ ರಚನೆ ಮಲ್ತೆರ್.<ref name="Oscar Shortlist">{{cite web |url=http://www.oscars.org/press/pressreleases/2012/20120118.html |title=9 Foreign Language Films Vie for Oscar |access-date=19 January 2012 |url-status=dead |archive-url=https://web.archive.org/web/20120518164323/http://www.oscars.org/press/pressreleases/2012/20120118.html |archive-date=18 May 2012 }}</ref> ಈ ಸಿನಿಮಾಡ್ ಅಬು (ಸಲೀಮ್ ಕುಮಾರ್) ಪನ್ಪಿನ ಒರಿ ಬಡ ಅಟ್ಟರ್ (ಒಂಜಿ ಬಗೆತ ಸುಗಂಧ ದ್ರವ್ಯ) ಮಾರಾಟ ಮಲ್ಪುನ ಕತೆ ಉಂಡು. ಜೀವನೊಡು ಅರೆನ ಒಂಜೇ ಮಲ್ಲ ಆಸೆ ಪಂಡ ಹಜ್ ಯಾತ್ರೆ ಮಲ್ಪುನವು. ಪವಿತ್ರ ಯಾತ್ರೆ ಮಲ್ಪುನ ಕನೊನು ನನಲಾತ್ ಸತ್ಯ ಆಯೆರೆ ಅಬು ಕಷ್ಟೊಡು ಬೇಲೆ ಮಲ್ಪುವೆರ್. ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬು ಬಿಡುಗಡೆ ಆಯೆರೆ ಸುಮಾರ್ ಒಂಜಿ ದಶಕೊಗು ದುಂಬು ಸುರು ಆಂಡ್. ಡಿಜಿಟಲ್ ಶೂಟಿಂಗ್ ತಂತ್ರಜ್ಞಾನೊನ್ ಬಳಸದ್ ಒಂಜಿ ತಿಂಗೊಲುದ ಅವಧಿಡ್ ಈ ಚಿತ್ರೊನು ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್. ಚಿತ್ರೀಕರಣ ನವೆಂಬರ್ ೭, ೨೦೧೦ಡ್ ಸುರು ಆಂಡ್. ತ್ರಿಶೂರು ಬೊಕ್ಕ ಕೊಜಿಕೋಡ್ ಚಿತ್ರದ ಪ್ರಮುಖ ಚಿತ್ರೀಕರಣ ಸ್ಥಳೊಲು. ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬು ಜೂನ್ ೨೪, ೨೦೧೧ಡ್ ಚಿತ್ರಮಂದಿರೊಲೆಡ್ ಬಿಡುಗಡೆ ಆಂಡ್. ಈ ಚಿತ್ರೊಗು ಮಸ್ತ್ ವಿಮರ್ಶಕೆರ್ನ ಮೆಚ್ಚುಗೆ ಬತ್ತ್ಂಡ್. ಈ ಸಿನಿಮಾದ ಕತೆ, ನಿರ್ದೇಶನ, ಅಭಿನಯ, ಛಾಯಾಗ್ರಹಣ ಬೊಕ್ಕ ಹಿನ್ನೆಲೆ ಸಂಗೀತೊಗು ವಿಶೇಷವಾದ್ ಮೆಚ್ಚುಗೆ ಬತ್ತ್ಂಡ್.<ref>[http://www.thehindu.com/news/cities/Kochi/article2659157.ece "‘Adaminte Makan Abu' to be remade in Hindi"]. ''[[The Hindu]]''. 25 November 2011. Retrieved 2 December 2011.</ref><ref>[https://web.archive.org/web/20130608083504/http://articles.timesofindia.indiatimes.com/2011-10-19/news-interviews/30297338_1_big-screen-special-screening-short-films "Everybody is becoming a critic today: Nikhil Advani"]. ''[[The Times of India]]''. 19 October 2011. Retrieved 11 November 2011.</ref> '''ಈ ಸಿನಿಮಾ ನಾಲ್ ರಾಷ್ಟ್ರೀಯ ಚಲನಚಿತ್ರ ಪ್ರಶಸ್ತಿಲೆನ್ ಗೆಂದಿನವು:''' * ಅತ್ಯುತ್ತಮ ಸಿನಿಮಾ * ಶ್ರೇಷ್ಠ ನಟ — ಸಲೀಂ ಕುಮಾರ್ * ಅತ್ಯುತ್ತಮ ಛಾಯಾಗ್ರಹಣ — ಮಧು ಅಂಬತ್ * ಎಡ್ಡೆ ಹಿನ್ನೆಲೆ ಸಂಗೀತ '''ಅತ್ತಂದೆ, ಈ ಸಿನಿಮಾ ನಾಲ್ ಕೇರಳ ರಾಜ್ಯ ಚಲನಚಿತ್ರ ಪ್ರಶಸ್ತಿಲೆನ್ ಗೆಂದಿನವು:''' * ಅತ್ಯುತ್ತಮ ಸಿನಿಮಾ * ಶ್ರೇಷ್ಠ ನಟ — ಸಲೀಂ ಕುಮಾರ್ * ಅತ್ಯುತ್ತಮ ಚಿತ್ರಕಥೆ * ಎಡ್ಡೆ ಹಿನ್ನೆಲೆ ಸಂಗೀತ ಅಡಮಿಂತೆ ಮಗನ್ ಅಬುನ್ 2011 ಅಕಾಡೆಮಿ ಪ್ರಶಸ್ತಿಗ್ ಅತ್ಯುತ್ತಮ ವಿದೇಶಿ ಭಾಷೆದ ಚಿತ್ರೊಗು ಭಾರತದ ಅಧಿಕೃತ ಪ್ರವೇಶ ಆದ್ ಕಡಪುಡುವೆರ್. ಆಂಡಲಾ, ಚಿತ್ರೊನು ಅಂತಿಮ ನಾಮನಿರ್ದೇಶನೊಗು ಆಯ್ಕೆ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. == ಕಥಾವಸ್ತು == ಅಬು ಬೊಕ್ಕ ಐಶುಮ್ಮ ಪಂಡ ಕೇರಳದ ಮಲಬಾರ್ ಪ್ರದೇಶೊಡು ಜೀವನ ಮಲ್ಪುನ ಒರಿ ಹಿರಿಯ ಮುಸ್ಲಿಂ ಜೋಡಿ. ಅಕ್ಲೆನ ಜೀವನೊಡು ಮಲ್ಲ ಆಸೆ ಪಂಡ ಮೆಕ್ಕಾಗ್ ಪೋದು ಹಜ್ ಯಾತ್ರೆ ಮಲ್ಪುನವು. ಈ ಕನೊನು ನನಲಾತ್ ನಡಪಾಯೆರೆ ಜೀವನದ ಎಲ್ಯ ಎಲ್ಯ ಅಗತ್ಯೊಲೆನ್ ಲಾ ತ್ಯಾಗ ಮಲ್ತ್ ದ್ ದುಡ್ಡುನ್ ಉಳಿತಾಯ ಮಲ್ಪುವೆರ್. ಅಬು ಮೆಕ್ಕಾಗ್ ಪೋಪಿನ ಕನಸ್ ಗ್ ದುಡ್ಡುನ್ ಮಸ್ತ್ ಮಿತವ್ಯಯೊಡು ಉಳಿತಾಯ ಮಲ್ಪುವೆರ್. ಕೇರಳೊಡ್ದ್ ವಲಸೆ ಬತ್ತಿನ ಗಲ್ಫ್ ಅಲೆತ ಭಾಗವಾದ್ ಅಕ್ಲೆನ ಮಗೆ ಸತ್ತರ್ ತನ್ನ ಕುಟುಂಬೊದೊಟ್ಟುಗು ಮಧ್ಯಪ್ರಾಚ್ಯೊಗು ವಲಸೆ ಪೋಯೆರ್. ಆಂಡ ಆರ್ ತನ್ನ ಹಿರಿಯ ಅಪ್ಪೆ ಅಮ್ಮೆರೆನ್ ಬಹುತೇಕ ಮದತೆರ್. ಏಳ್ ವರ್ಸೊದ ಜೋಡಿ, ಆ ವರ್ಷ ಹಜ್ ಯಾತ್ರೆಗ್ ಪೋಯೆರೆ ದುಡ್ಡು ಉಳಿತಾಯ ಮಲ್ತೊಂದುಲ್ಲೆರ್. ಅಬು ಅತ್ತಾರ್ (ಪುಷ್ಪೊಲೆಡ್ದ್ ಮಲ್ಪುನ ಸುಗಂಧ ದ್ರವ್ಯ), ಧಾರ್ಮಿಕ ಪುಸ್ತಕೊಲು ಬೊಕ್ಕ ಅನಗತ್ಯ ಉನಾನಿ ಮರ್ದ್ ಲೆನ್ ಮಾರಾಟ ಮಲ್ಪುವೆರ್. ಐಶುಮ್ಮ ಜಾನುವಾರು ಬೊಕ್ಕ ಕೋರಿಲೆನ್ ಸಾಕಾದ್ ಕುಟುಂಬದ ಆದಾಯೊಗು ಕೊಡುಗೆ ಕೊರ್ಪೆರ್ ಬೊಕ್ಕ ಅರೆನ ಕಂಡನಿಗ್ ಅಕ್ಲೆನ ಸಾಮಾನ್ಯ ಕನೊನು ಸಾಕಾರ ಮಲ್ಪೆರೆ ಸಹಾಯ ಮಲ್ಪುವೆರ್. ಅಬು, ಆರ್ ಮಾರಾಟ ಮಲ್ಪುನ ಸಾಂಪ್ರದಾಯಿಕ ವಸ್ತುಲೆನಲೆಕನೆ, ತ್ವರಿತವಾದ್ ಬದಲಾವೊಂದುಪ್ಪುನ ಆಧುನಿಕ ಲೋಕೊಡು ಪಿರಾಕ್ದ ಬೊಕ್ಕ ಅಪ್ರಸ್ತುತವಾದ್ ತೋಜುಂಡು. ಒರಿ ಶಾಲೆದ ಶಿಕ್ಷಕೆರ್, ಅಬುನ ಒರಿ ಸ್ನೇಹಿತೆ, ಅಬುನ ಅಗತ್ಯೊದ ಪೊರ್ತುಡು ಅರೆಗ್ ಸಹಾಯ ಮಲ್ಪೆರೆ ಬರ್ಪೆರ್. ಒಂಜಿ ಸ್ಥಳೀಯ ಚಾ ಅಂಗಡಿದ ಮಾಲೀಕೆರ್ ಆಯಿನ ಹೈದರ್, ಅಬುನ ಪರಿಸ್ಥಿತಿಗ್ ಸಹಾನುಭೂತಿ ತೋಜಾವೆರ್. ಬೊಕ್ಕೊಂಜಿ ಕಡೆಟ್, ‘ಉಸ್ತಾದ್’ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಮಾತ್ರ ಪುದರ್ ಪೋಯಿನ ಒರಿ ನಿಗೂಢ ಸಾಧು ಅಬುಗು ಪ್ರಾಮಾಣಿಕ ಸಲಹೆನ್ ಕೊರ್ಪೆರ್. ಅಬುನ ಪರಿಸ್ಥಿತಿನ್ ಅರ್ಥ ಮಲ್ತೊಂದು, ಅಬುನ ಪರಿಸ್ಥಿತಿನ್ ಅರ್ಥ ಮಲ್ತೊಂದು, ಮಸ್ತ್ ಎಡ್ಡೆ ಉದ್ದೇಶದ ಜನಕುಲು ಅರೆಗ್ ದುಡ್ಡು ಸಾಲ ಕೊರ್ಪಿನ ಒಫರ್ ಮಲ್ಪುವೆರ್. ಆಂಡ ಅಬು ಏರೆಡಲಾ ದುಡ್ಡು ಸಾಲ ದೆತೊನೆರೆ ನಿರಾಕರಣೆ ಮಲ್ಪುವೆರ್ ದಾಯೆ ಪಂಡ ಅವು ಸ್ವೀಕಾರ ಮಲ್ತಿನ ಇಸ್ಲಾಮಿಕ್ ಆಚರಣೆಲೆಗ್ ವಿರುದ್ಧವಾದುಂಡು. ಅಬು ಮಲ್ಲೆ ಆನಗ, ಹಜ್ ಯಾತ್ರೆ ಮಲ್ಪುನ ತುರ್ತು ಬೊಕ್ಕ ಹತಾಶೆ ಏರುಂಡು. ಅಕೇರಿಗ್, ಬೇಜಾರು ಬೊಕ್ಕ ಬೇಜಾರುಡ್ದ್, ಆರ್ ತನ್ನ ಇಲ್ಲದ ಎದುರುಡು ಉಪ್ಪುನ ಪೆತ್ತ ಬೊಕ್ಕ ಪಿರಾಕ್ದ ಜಾಕ್ ಫ್ರೂಟ್ ಮರನ್ ಮಾರಾಟ ಮಲ್ಪುವೆರ್. ಒರಿ ಟ್ರಾವೆಲ್ ಏಜೆನ್ಸಿ ಮ್ಯಾನೇಜರ್ ಅಬುಗು ಯಾತ್ರೆಗ್ ಬೋಡಾಪಿನ ವಿಮಾನ ಟಿಕೆಟ್ ಬೊಕ್ಕ ಬೇತೆ ದಾಖಲೆಲೆನ್ ದೆತೊನೆರೆ ಸಹಾಯ ಮಲ್ಪುವೆರ್. ಸುರುಟು, ಒರಿ ಪೊಲೀಸ್ ಅಧಿಕಾರಿ ಅಬುನ ಪಾಸ್ಪೋರ್ಟ್ ಅರ್ಜಿನ್ ಖಂಡಿಸರೆ ಪ್ರಯತ್ನ ಮಲ್ಪುವೆರ್. ಆಂಡ ಅಯನ ಸಾಮಾನ್ಯ ಲಂಚ ದೆತೊನುನ ಬುಕ್ಕೊ, ಅಬುಗು ಸಹಾಯ ಮಲ್ಪೆರೆ ಒಪ್ಪಿಗೆ ಮಲ್ಪುವೆ. ದಂಪತಿಲು ಹಜ್ ಯಾತ್ರೆದ ತಯಾರಿನ್ ಸುರು ಮಲ್ಪುವೆರ್. ಒಂಜಿ ಟ್ರಾವೆಲ್ ಏಜೆನ್ಸಿ ನಡಪಾವುನ ಹಜ್ ತಯಾರಿ ತರಗತಿಲೆಗ್ ಹಾಜರಾತಿನವು, ಮೆನಿಂಗೊಕಾಕಲ್ ಮೆನಿಂಜೈಟಿಸ್ ಗ್ ಲಸಿಕೆ ಪಾಡುನವು, ಬೊಕ್ಕ ಪೊಸ ಇಹ್ರಾಮ್ ಬಟ್ಟೆಲೆನ್ ಕೊಂಡೊನುನವು ಉಂದೆಟ್ ಸೇರ್ದ್ ಉಂಡು. ಹಜ್ ಗ್ ಪೋಪಿನ ದುಂಬು, ಅಬು ತನ್ನ ಪಿರಾಕ್ದ ಸಾಲೊಲೆನ್ ಒಂಜಿ ಒಂಜಿ ಮಲ್ತ್ ದ್ ಕೊರ್ಪೆ. ದುಂಬು ಜಗಳ ಆಯಿನ ತನ್ನ ಪಿರಾಕ್ದ ನೆರೆಕರೆತ ಸುಲೈಮಾನ್ ಗ್ ಕ್ಷಮೆ ಕೋರೆರೆ ಆರ್ ಮಸ್ತ್ ಮೈಲ್ ದೂರೊಗು ಪೋಪೆರ್. ಪಾಸ್ಪೋರ್ಟ್ ಬೊಕ್ಕ ವಿಮಾನ ಟಿಕೆಟ್ ರೆಡಿ ಆತ್ಂಡ್. ಪ್ರಯಾಣದ ಸುರುತ ಬಿಂದು ಆಯಿನ ಕೊಜಿಕೋಡ್ ಗ್ ಒಂಜಿ ಬಸ್ ಸವಾರಿ ಮಾತ್ರ ಉಂಡು. ಅಂಚನೆ, ಅಬುನ ಜಾಕ್ ಫ್ರೂಟ್ ಮರನ್ ಕೊಂಡುನ ಕಟ್ಟೆದ ಮಾಲೀಕೆರ್, ಮರಕ್ ದುಡ್ಡು ಕೊರ್ನಗ, ಮರತ ಕಾಂಡ ಕೊಳೆತ ಬೊಕ್ಕ ನಿಷ್ಪ್ರಯೋಜಕ ಆದುಂಡು ಪನ್ಪೆರ್. ಆಂಡಲಾ, ಅಬುನ ಉದ್ದೇಶ ಎಡ್ಡೆ ಆಯಿನೆಡ್ದಾವರ ಆರ್ ದುಡ್ಡುನ್ ಸ್ವೀಕಾರ ಮಲ್ಪೊಡು ಪಂದ್ ಒತ್ತಾಯ ಮಲ್ಪುವೆರ್. ಆಂಡ ಅಬು ದುಡ್ಡುನ್ ನಿರಾಕರಣೆ ಮಲ್ಪುವೆ. ಉಂದು ಹಲಾಲ್ ಅತ್ತ್ ಪಂದ್ ಆರ್ ಪನ್ಪೆರ್ ಬೊಕ್ಕ ಅವೆನ್ ಸ್ವೀಕಾರ ಮಲ್ಪುನಗ ಅಲ್ಲಾಹ್ ಗ್ ಕೋಪ ಬರ್ಪುಂಡು ಪಂದ್ ಆರ್ ಪನ್ಪೆರ್. ಹಜ್ ದ ಕನ ನಿಜ ಆಪುಜಿ ಪನ್ಪಿನವು ಸ್ಪಷ್ಟ ಆನಗ, ಅಬುಗು ಬೇತೆ ಒಂಜಿ ವಿಷಯ ತೆರಿಯುಂಡು. ಮರಕ್ಲಾ ಜೀವ ಉಂಡು; ಅರೆನ ಇಲ್ಲ್ ದ ಎದುರುದ ಮರನ್ ಕಡ್ತ್ ದ್ ದೀನಗ ಅಯಿಟ್ ಉಪ್ಪುನ ಮಸ್ತ್ ಜೀವಿಲು ಸೈತ್ ಂಡ್ ಪಂದ್ ಅರೆಗ್ ಅನಿಸವುಂಡು. ಅಬು ತನ್ನನ್ ಹಜ್ಜ್ ಗ್ ಪೋಯೆರೆ ಸಾಧ್ಯ ಆಯಿಜಿ ಪನ್ಪಿನೆಕ್ ಒಂಜಿ ಕಾರಣ ಆದುಪ್ಪೊಲಿ ಪಂದ್ ಅಬು ಯೋಚನೆ ಮಲ್ಪುವೆರ್. ಹಜ್ ದ ಬೆಳಗ್ಗೆ, ಅಬು ತನ್ನ ಮದಿಮೆ ಆಯಿನ ಐಶುಮ್ಮಗ್ ಪನ್ಪೆರ್, "ಮುಕ್ಲೆ ವರ್ಷ ಪೋವೊಡು." ಅಪಗ ಆರ್ ಒಂಜಿ ಪೊಸ ಜಾಕ್ ಫ್ರೂಟ್ ಮರನ್ ನಡ್ಪೆರ್. ಹಜ್ ಸುರು ಆಪಿನ ಅದೇ ಬೆಳಗ್ಗೆ ಅಬು ಮಸೀದಿಗ್ ಪ್ರಾರ್ಥನೆ ಮಲ್ಪೆರೆ ಪೋಪಿನೆಟ್ ಚಿತ್ರ ಮುಗಿಪುಂಡು. == ಪಾತ್ರವರ್ಗ == * ಸಲೀಂ ಕುಮಾರ್ — ಅಬು<ref>{{cite news|url=http://articles.timesofindia.indiatimes.com/2011-05-20/chennai/29564006_1_haj-pilgrimage-malayalam-film-first-film|archive-url=https://web.archive.org/web/20121105144447/http://articles.timesofindia.indiatimes.com/2011-05-20/chennai/29564006_1_haj-pilgrimage-malayalam-film-first-film|url-status=dead|archive-date=5 November 2012|author=T. Ramavarman |title=Debut director makes a mark |date=19 May 2011|location=India|work=[[The Times of India]]|access-date=25 May 2011}}</ref><ref name="Mangalam report">{{cite news|url=http://mangalam.com/index.php?page=detail&nid=430322&lang=malayala |title=കാല്‍പ്പനികതയുടെ കൈയൊപ്പ്‌|language=ml|author=K. Sujith|work=[[Mangalam]] |date=29 May 2011|access-date=8 June 2011}}</ref> * ಜರೀನಾ ವಹಾಬ್ — ಐಶುಮ್ಮಾ * ಆದರ್ಶ್ ಆಂಟೋ — ಸತ್ತಾರ್ * ಮುಕೇಶ್ — ಅಶ್ರಫ್ * ಕಲಾಭವನ್ ಮಣಿ — ಜಾನ್ಸನ್ * ತಂಪಿ ಆಂಟನಿ — ಉಸ್ತಾದ್ * ಸುರಾಜ್ ವೆಂಜಾರಮೂಡು — ಹೈದರ್ * ನೆಡುಮೂಡಿ ವೇಣು — ಗೋವಿಂದನ್ ಮಾಸ್ಟರ್ * ಟಿ. ಎಸ್. ರಾಜು — ಮಾಲಿಯೆಕ್ಕಲ್ ಅಸೈನಾರ್ ಹಾಜಿ * ಜಾಫರ್ ಇಡುಕ್ಕಿ — ಛಾಯಾಗ್ರಾಹಕ * ಶಶಿ ಕಳಿಂಗ — ಕಬೀರ್ * ಎಂ. ಆರ್. ಗೋಪಕುಮಾರ್ — ಸುಲೈಮಾನ್ * ಅಂಬಿಕಾ ಮೋಹನ್ — ನಳಿನಿ * ನಿಶಾ ಸಾರಂಗಿ — ರಬಿಯಾ * ಮುರಳಿ — ಅಮೀರ್ * ಮೊಹಮ್ಮದ್ ಮಯನಾಡ್ — ಕೈತಿ * ವಿನೋದ್ ಕೋವೂರ್ — ಮೊಯ್ದೀನ್ * ಶಶಿಧರನ್ ಮಟ್ಟನ್ನೂರ್ — ಮಮ್ಮುಂಜಿ<ref>{{cite news |url=http://mangalam.com/index.php?page=detail&nid=427762&lang=malayalam|title=ചില്ലറ മാത്രം വാങ്ങി അഭിനയിച്ചു|language=ml|date=22 May 2011|work=[[Mangalam]]|access-date=25 May 2011}}</ref><ref>{{cite news|url=http://www.madhyamam.com/news/79551/110520|title=അബുവിന്റെ പെട്ടി സലിംകുമാറിന്റെ പ്രതിഫലം|language=ml|date=20 May 2011|work=[[Madhyamam]]|access-date=26 May 2011|archive-url=https://web.archive.org/web/20110602230925/http://madhyamam.com/news/79551/110520|archive-date=2 June 2011|url-status=dead}}</ref> == ಉತ್ಪಾದನೆ == '''ಬುಲೆಚ್ಚಿಲ್''' ಆಡಮಿಂತೆ ಮಗನ್ ಅಬು ಸಲೀಮ್ ಅಹ್ಮದ್ ನ ಮೊದಲ ಪೂರ್ಣ ಚಿತ್ರ. ಆ ಕತೆ ಅರೆನ ಮನಸ್ಸ್ ಡ್ ಪತ್ತ್ ವರ್ಸೊಡ್ದ್ ಜಾಸ್ತಿ ಉಂಡು.<ref name="Creation is often traumatic">{{cite news|url=http://newindianexpress.com/entertainment/interviews/article444004.ece |archive-url=https://web.archive.org/web/20160304095715/http://www.newindianexpress.com/entertainment/interviews/article444004.ece |url-status=dead |archive-date=4 March 2016 |title=Creation is often traumatic|author=Shankaran Malini|date=23 June 2011|work=The New Indian Express |location=India|access-date=13 November 2011}}</ref> ಚಿತ್ರದ ಅಭಿವೃದ್ಧಿ ಬಿಡುಗಡೆ ಆಯೆರೆ ಸುಮಾರ್ ಒಂಜಿ ದಶಕೊಗು ದುಂಬು ಸುರು ಆಂಡ್. ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬು ಸಲೀಮ್ ಅಹ್ಮದ್ ನ ನಿರ್ದೇಶನದ ಸುರುತ ಪಾತ್ರ, ಬೊಕ್ಕ ಈ ಕತೆ ಅರೆನ ಮನಸ್ಸ್ ಡ್ ಮಸ್ತ್ ವರ್ಷೊಡ್ದಿಂಚಿ ಇತ್ತ್ಂಡ್. 2000 ದಶಕದ ಸುರುಟು ಕಾಲೇಜ್ ಶಿಕ್ಷಣ ಮುಗಿತ್ ಬೊಕ್ಕ, ಅಹ್ಮದ್ ಚಿತ್ರರಂಗೊಗು ಪ್ರವೇಶ ಮಲ್ಪುನ ಕನಸ್ ತೋಜಾಯೆರ್. ಆಂಡ ಸಿನಿಮಾ ರಂಗೊಗು ಪ್ರವೇಶ ಮಲ್ಪೆರೆ ಕಷ್ಟ ಆಂಡ್, ಅಂಚಾದ್ ಟ್ರಾವೆಲ್ ಕನ್ಸಲ್ಟೆಂಟ್ ಆದ್ ಬೇಲೆ ಮಲ್ಪೆರೆ ಸುರು ಮಲ್ತೆರ್. ಆ ಕಾಲೊಡು ಆರ್ ಕೇಂಡಿನ ಕತೆಕುಲು ಬೊಕ್ಕ ಆರ್ ತೂಯಿನ ಜನಕುಲು ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬುಗು ಕತೆತ ಬೀಜೊಲೆನ್ ಬುಳೆಪಾಯೆರ್.<ref>{{cite web|url=http://www.keralamax.com/movies/aadaaminte-makan-abu-salim-kumar-to-do-the-lead-role-in-the-film/ |title=Adaminte Makan Abu – Salim Kumar to do the lead role in the film! |date=3 November 2010 |publisher=Keralmax.com |access-date=30 May 2011 |url-status=dead |archive-url=https://web.archive.org/web/20120404174434/http://keralamax.com/movies/aadaaminte-makan-abu-salim-kumar-to-do-the-lead-role-in-the-film/ |archive-date=4 April 2012 }}</ref> ತ್ರಿಶೂರುದ ಅಥಾನಿಡ್ ಒಂಜಿ ಇಲ್ಲಡ್ದ್ ಪ್ರಮುಖ ವಿಭಾಗೊಲೆನ್ ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್. ಈ ಸೆಟ್ ನ್ ಕಲಾ ನಿರ್ದೇಶಕೆರ್ ಜ್ಯೋತಿಶ್ {{INRConvert|2|l|1|nolink=yes}} ದ ಶೂಸ್ಟ್ರಿಂಗ್ ಡ್ ಮಲ್ತೆರ್.<ref>{{cite news |url=http://www.manoramaonline.com/cgi-bin/MMOnline.dll/portal/ep/malayalamContentView.do?programId=1073753765&contentId=9362000&tabId=11|title=ആദാമിന്റെ വീടൊരുക്കിയ ആനന്ദത്തോടെ ജ്യോതിഷ് |language=ml|date=21 May 2011|work=[[Malayala Manorama]]|access-date=25 May 2011}}</ref> ಈ ಸಿನಿಮಾದ ಪ್ರಮುಖ ಪಾತ್ರೊಲು ಅಬು ಬೊಕ್ಕ ಐಶುಮ್ಮ ನಿಜ ಜೀವನೊಡು ನಡತಿನ ಒಂಜಿ ಜೋಡಿಡ್ದ್ ಸ್ಫೂರ್ತಿ ಪಡೆಯಿನವು. ಜೆಡ್ಡಾ ವಿಮಾನ ನಿಲ್ದಾಣೊಡು ಕುಟುಂಬೊಗು ಕನತಿನ ಉಡುಗೊರೆಲೆಡ್ದ್ ಉಂಡಾತಿನ ಅತಿಯಾದ ಸಾಮಾನುಲೆನ್ ಸರಿ ಮಲ್ಪೆರೆ ಪ್ರಯತ್ನ ಮಲ್ಪುನಗ, ಅಹ್ಮದ್ ಉಮ್ರಾ ಯಾತ್ರೆಡ್ದ್ ವಾಪಸ್ ಬರ್ಪಿನ ಒರಿ ಹಿರಿಯ ಜೋಡಿನ್ ತೂಯೆರ್. ಅಕುಲು ಬಾರಿ ಸರಳ ಜೀವನ ಮಲ್ತೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಹಜ್ ಯಾತ್ರೆಗ್ ಪೋಯೆರೆ ಆಸೆಡ್ ದುಡ್ಡು ಉಳಿತಾಯ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಆಂಡ ಅಕುಲು ಹಜ್ ಯಾತ್ರೆಗ್ ಪೋಯೆರೆ ಸಾಧ್ಯ ಇಜ್ಜಿ ಪನ್ಪಿನ ಭಯೊಡು ಅಕುಲು ಅಕೇರಿಗ್ ಉಮ್ರಾ ಯಾತ್ರೆಡ್ ನೆಲೆಯಾಯೆರ್.<ref>{{cite news|language=ml |work=[[Mathrubhumi]] |date=20 May 2011 |access-date=8 December 2011 |title=മലയാളത്തിലെ നേട്ടം ആവേശകരം- മധു അമ്പാട്ട് |url=http://www.mathrubhumi.com/online/malayalam/news/story/945720/2011-05-20/entertainment |url-status=dead |archive-url=https://web.archive.org/web/20131012052744/http://www.mathrubhumi.com/online/malayalam/news/story/945720/2011-05-20/entertainment |archive-date=12 October 2013 }}</ref> ಅಹ್ಮದ್ ಜೋಡಿನ್ ಸ್ನೇಹಿತೆರಾದ್ ಅಕ್ಲೆನ ವಿಮಾನ ಸಮಸ್ಯೆ ಬೊಕ್ಕ ಹೋಟೆಲ್ ವಸತಿಗ್ ಸಹಾಯ ಮಲ್ತೆರ್. ಅಕ್ಲೆನೊಟ್ಟುಗು ಉಪ್ಪುನಗ, ಅಹ್ಮದ್ ಅಕ್ಲೆನ ಸ್ಥಿರ ಮನೋಭಾವ, ಜೀವನದ ಅನುಭವೊಲು ಬೊಕ್ಕ ಸದ್ಗುಣೊಲೆನ ಬಗ್ಗೆ ಮಸ್ತ್ ತೆರಿನಕುಲು. ಅಹ್ಮದ್ ನ ಊರು ಅಬುಟಿಕ್ಕದ ಒರಿ ವ್ಯಕ್ತಿಲಾ ಈ ಚಿತ್ರದ ಮುಖ್ಯ ಪಾತ್ರ ಅಬುಗು ಪ್ರಮುಖ ಸ್ಫೂರ್ತಿ ಆಯೆರ್. ಅಬುಟಿಕ್ಕ ಚಿತ್ರೊಡು ಅಬು ಪಾತ್ರೊದಲೆಕನೆ ತೋಜುಂಡು ಬೊಕ್ಕ ವರ್ತನೆ ಮಲ್ಪುಂಡು. ಅಹ್ಮದ್ ನ ಪ್ರಕಾರ, ಉಮ್ರಾ ಯಾತ್ರೆಡ್ ತೂಯಿನ ಹಿರಿಯ ಜೋಕುಲೆನ ಅನುಭವೊಲು ಬೊಕ್ಕ ಅರೆನ ಊರುದ ಅಬುತ್ತಿಕ್ಕನ ವ್ಯಕ್ತಿತ್ವ ಒಟ್ಟು ಸೇರ್ದ್ ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬು ಪನ್ಪಿನ ಸಿನಿಮಾನ್ ರಚನೆ ಮಲ್ತೆರ್.<ref>{{cite news|work=The New Indian Express |publisher=[[IBN Live]] |date=3 December 2011|time=11:14&nbsp;am (IST)|location=Kochi, India|title=The glory of Mother Mary |url=http://ibnlive.in.com/news/the-glory-of-mother-mary/208266-60-122.html|archive-url=https://archive.today/20120708213346/http://ibnlive.in.com/news/the-glory-of-mother-mary/208266-60-122.html|url-status=dead|archive-date=8 July 2012|access-date=8 December 2011}}</ref> ಕಾಲೇಜಿದ ಕಾಲೊಡು, ಅಹ್ಮದ್ ಮೂಜಿ ಮೂಲ ಕತೆಕುಲೆನ್ ಬರೆಯೆರ್. ಒಂಜಿ ಕೇರಳದ ಸ್ಥಳೀಯ ಶಾಲೆನ್ ಆಧಾರ ಮಲ್ತ್ದ್, ಬೊಕ್ಕೊಂಜಿ ಥ್ರಿಲ್ಲರ್ ಬೊಕ್ಕ ಮೂಜಿನೆ ಅಬುನ ಕತೆ. ಅಬುನ ಕತೆ ಮಸ್ತ್ ಜನಕ್ಲೆಗ್ ಇಷ್ಟ ಆಯಿನೆಡ್ದಾವರ, ಆರ್ ಅವೆನ್ ತನ್ನ ಸುರುತ ಸಿನಿಮಾ ಆದ್ ನಿರ್ದೇಶನ ಮಲ್ಪೆರೆ ನಿರ್ಧರಿಸಯೆರ್. ಅಂಚನೆ, ಅರೆನ ಸುರುತ ಸಿನಿಮಾ ಒಂಜಿ ಸಂದೇಶೊನು ಕೊರ್ದು ಪ್ರಭಾವಶಾಲಿ ಆವೊಡು ಪಂದ್ ಆರ್ ಬಯಕೆ ಮಲ್ತೆರ್. ಅಹ್ಮದ್ ಗ್ ಅಂತಿಮ ಸ್ಕ್ರಿಪ್ಟ್ ತಯಾರ್ ಮಲ್ಪೆರೆ ಒಂಜಿ ತಿಂಗೊಲು ಬೋಡಾಂಡ್. ಚಿತ್ರೊಗು ಒರಿ ನಿರ್ಮಾಪಕೆ ಅತ್ತ್ಂಡ ಹಣಕಾಸುದಾರೆನ್ ಕಂಡುಕೊನೊಡು ಪನ್ಪಿನವು ಆರ್ ಎದುರಿಸೊಂದಿತ್ತಿನ ಮಲ್ಲ ಸವಾಲ್. ಇಂಚಿನ ಒಂಜಿ ವಾಣಿಜ್ಯೇತರ ಚಿತ್ರೊಗು ಹಣಕಾಸು ಕೊರ್ರೆ ಏರ್ಲಾ ದುಂಬು ಬತ್ತಿಜೆರ್. ಅಂಚಾದ್ ದೂರದರ್ಶನೊಡು ಬೇಲೆ ಮಲ್ತೊಂದು ತನ್ನ ಸಂಬಳನ್ ಉಳಿತಾಯ ಮಲ್ಪೆರೆ ಸುರು ಮಲ್ತೆರ್. ಆಂಡ ಆ ದುಡ್ಡು ಸಾಕಾಗುಜಿ. ಅಂಚನೆ, ಆರ್ ತನ್ನ ಸ್ನೇಹಿತೆ ಬೊಕ್ಕ ನಿರ್ಮಾಪಕೆ ಅಶ್ರಫ್ ಬೇಡಿನ್ ತೂಯೆರ್. ಅಶ್ರಫ್ ಬೇದಿ ಶಾಂಬೂ ಚಿತ್ರದ ನಿರ್ಮಾಪಕೆರ್ನ ಒರಿಯೆ ಆದಿತ್ತೆರ್, ಉಂದೆಕ್ ಸಲೀಮ್ ಅಹ್ಮದ್ ಚಿತ್ರಕಥೆಕಾರೆ ಆದ್ ಬೇಲೆ ಮಲ್ತೆರ್. ಶ್ಯಾಂಬೂ ಚಿತ್ರದ ಶೂಟಿಂಗ್ ದ ಪೊರ್ತುಡು ಸಲೀಮ್ ಅಹ್ಮದ್ ಅಶ್ರಫ್ ಬೇಡಿನೊಟ್ಟುಗು ಈ ಕತೆನ್ ಚರ್ಚೆ ಮಲ್ತೊಂದು ಇತ್ತೆರ್. ಈ ಕತೆತ ಬಗ್ಗೆ ದುಂಬೇ ಗೊತ್ತಿತ್ತಿನ ಅಶ್ರಫ್ ಬೇಡಿ, ಸಲೀಮ್ ಅಹ್ಮದ್ ನ ಒಟ್ಟುಗು ಈ ಚಿತ್ರೊನು ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ಒಪ್ಪಿಗೆ ಕೊರ್ತೆರ್. == ಪಾತ್ರೊಲು == ಚಿತ್ರೊಗು ನಟೆರ್ ಬೊಕ್ಕ ತಾಂತ್ರಿಕ ತಂಡೊನು ಅಂತಿಮ ಮಲ್ಪುನ ದುಂಬು ಅಶ್ರಫ್ ಬೇದಿ ಬೊಕ್ಕ ಸಲೀಮ್ ಅಹ್ಮದ್ ಮಸ್ತ್ ಸಂಶೋಧನೆ ಮಲ್ತೆರ್.<ref name="Preview by The Hindu">Jos Thomas. (1 December 2011). {{Deprecated archive|url=https://archive.today/20130204010059/http://www.thehindu.com/news/states/kerala/article2677160.ece|title="Preview: Aadaminte Makan Abu"}}. ''[[The Hindu]]''. Retrieved 8 December 2011.</ref> ಸಲೀಂ ಕುಮಾರ್ ನ್ ಮುಖ್ಯ ಪಾತ್ರೊಡು ಆಯ್ಕೆ ಮಲ್ತಿನಗ, ಒಂಜಿ ಸಾಮಾನ್ಯ ನಾಯಕನ ಗ್ಲಾಮರ್ ಇಜ್ಜಂದಿನ ನಟನೊಟ್ಟುಗು ಗಂಭೀರ ವಿಷಯೊಡು ಸಿನಿಮಾ ಮಲ್ಪುನ ಅಪಾಯೊನು ತೆಗೆದುಕೊಳ್ಳೆರೆ ನಿರ್ಧರಿಸಯೆರ್. ಆಂಡ ಚಿತ್ರದ ಅಂತಿಮ ಫಲಿತಾಂಶದ ಮಹತ್ವೊಗು ಹೋಲಿಕೆ ಮಲ್ಪುನಗ ಅಪಾಯ ಎಲ್ಯ ಪಂಡ್ದ್ ಅರೆಗ್ ನಂಬಿಕೆ ಇತ್ತ್ಂಡ್. ಅಹ್ಮದ್ 2010ಡ್ ಅತ್ಯುತ್ತಮ ನಟ ಚಿತ್ರದ ಚಿತ್ರೀಕರಣದ ಪೊರ್ತುಡು ಸಲೀಮ್ ಕುಮಾರ್ ನ್ ತೂದು ಅಬು ಪಾತ್ರ ಮಲ್ಪೆರೆ ಮನವರಿಕೆ ಮಲ್ತೆರ್. ಅಹ್ಮದ್ ನ ಪ್ರಕಾರ, ಸಲೀಮ್ ಕುಮಾರ್ ನ ಜನಪ್ರಿಯತೆ ಬೊಕ್ಕ ಬೇತೆ ಬೇತೆ ಪಾತ್ರೊಲೆನ್ ಮಲ್ಪುನ ಸಾಮರ್ಥ್ಯೊಗು ಕಾರಣವಾದ್ ಈ ಪಾತ್ರೊಗು ಒರಿ ಸ್ವಾಭಾವಿಕ ಆಯ್ಕೆ ಆಯೆರ್. ಕೇರಳ ಕೆಫೆ ಸಂಕಲನ ಬ್ರಿಡ್ಜ್ ದ ಪತ್ತ್ ಕತೆಟ್ ತನ್ನ ಹಿರಿಯ ಅಮ್ಮೆನ್ ಬುಡ್ದು ಪೋಯೆರೆ ಬಲವಂತ ಆಯಿನ ಮಗೆ ಆದ್ ಸಲೀಮ್ ಕುಮಾರ್ ನ ಗಮನಾರ್ಹ ನಟನೆಲಾ ಅಹ್ಮದ್ ನ್ ಆಯ್ಕೆ ಮಲ್ಪೆರೆ ಒಂಜಿ ಪ್ರಮುಖ ಕಾರಣ ಆಂಡ್. ಆಂಡಲಾ, ಸಲೀಮ್ ಕುಮಾರ್ ಸುರುಟು ಚಿತ್ರೊನು ತನ್ನ ಭುಜೊಡು ಕರೆತೊಂದು ಪೋಪಿನ ಒರಿ ನಾಯಕನ ಪಾತ್ರೊನ್ ಮಲ್ಪುನ ಬಗ್ಗೆ ಸಂದೇಹೊಡು ಇತ್ತೆರ್. ಅಹ್ಮದ್ ಈ ಸಿನಿಮಾಡ್ ಒಂಜಿ ಎಡ್ಡೆ ತಾಂತ್ರಿಕ ತಂಡ ಉಪ್ಪುಂಡು ಪಂದ್ ಭರವಸೆ ಕೊರ್ತೆರ್. ಛಾಯಾಗ್ರಹಣೊಗು ಅನುಭವಿ ಛಾಯಾಗ್ರಾಹಕೆ ಮಧು ಅಂಬತ್ ಬೊಕ್ಕ ಸಂಗೀತೊಗು ಐಸಾಕ್ ಥಾಮಸ್ ಕೊಟ್ಟುಕಪ್ಪಳ್ಳಿನ್ ನೇಮಕ ಮಲ್ತೆರ್. ಸಾಮಾನ್ಯವಾದ್ ಹಾಸ್ಯ ಪಾತ್ರೊಗು ಪುದರ್ ಪೋಯಿನ ಸಲೀಂ ಕುಮಾರ್ ಅಬು ಪಾತ್ರೊಗು ವಿಶೇಷ ತಯಾರಿ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಅಂಚನೆ, ಚಿತ್ರೊಡು ನಟನೆ ಮಲ್ತಿನೆಕ್ ಆರ್ ಏರ್ಲಾ ಪಾರಿತೋಷಿಕೊನು ಪಡೆಯೊಂದಿಜ್ಜೆರ್. ಕ್ಯಾಲೆಂಡರ್ ಸಿನಿಮಾದ ಮೂಲಕ ಮಲಯಾಳ ಸಿನಿಮಾಗ್ ಪಿರ ಬತ್ತಿನ ಜರೀನಾ ವಹಾಬ್ ಈ ಸಿನಿಮಾಡ್ ಮಹಿಳಾ ನಾಯಕಿ ಪಾತ್ರೊನ್ ಮಲ್ದೆರ್. ಅಹ್ಮದ್ ನ ಪ್ರಕಾರ, ಜರೀನಾ ವಹಾಬ್ ನ್ ಆಯ್ಕೆ ಮಲ್ಪುನ ಮುಖ್ಯ ಕಾರಣ ಪಂಡ ಅರೆಗ್ ಮಲಬಾರ್ ಮುಸ್ಲಿಂ ಮಹಿಳೆನ ಮೋನೆದ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್. ಸಲೀಂ ಕುಮಾರ್ ಬೊಕ್ಕ ಜರೀನಾ ವಹಾಬ್ ನಕುಲೆನೊಟ್ಟುಗು ಈ ಚಿತ್ರೊಡು ಕಾಲಭವನ್ ಮಣಿ, ಮುಕೇಶ್, ಸೂರಜ್ ವೆಂಜಾರಮೂಡು, ನೆಡುಮುಡಿ ವೇಣು ಬೊಕ್ಕ ಜಾಫರ್ ಇಡುಕ್ಕಿ ಸೇರ್ದ್ ಕೆಲವು ಪ್ರಮುಖ ನಟೆರ್ಲಾ ನಟನೆ ಮಲ್ತೆರ್. ಅಹ್ಮದ್ ತನ್ನ ಸುರುತ ಚಿತ್ರೊಡ್ದ್ ಇಂಡಸ್ಟ್ರಿಡ್ ಉಪ್ಪುನ ಎಡ್ಡೆ ಪ್ರತಿಭೆಲೆನೊಟ್ಟುಗು ಬೇಲೆ ಮಲ್ಪೆರೆ ಬಯಕೆ ಮಲ್ತೆರ್. ಚಿತ್ರದ ಕತೆಟ್ ಕಲಾವಿದೆರ್ ಬೊಕ್ಕ ತಂತ್ರಜ್ಞೆರೆನ ಆಸಕ್ತಿಡ್ದ್ ಉಂದೆನ್ ಸಾಧಿಸೆರೆ ಸುಲಭ ಆಂಡ್. ಮಧು ಅಂಬತ್ ಸುಮಾರ್ 12 ವರ್ಸೊದ ಅಂತರೊಡು ಒಂಜಿ ಮಲಯಾಳ ಸಿನಿಮಾದ ಛಾಯಾಗ್ರಹಣೊಗು ಪಿರ ಬತ್ತೆರ್. == ಚಿತ್ರೀಕರಣ == ಮಾತಾ ನಟೆರೆನ ಮೇಕಪ್ ನ್ ಪಟ್ಟಣಂ ರಶೀದ್ ಮಲ್ತೆರ್. ಸುರುಟು ಆರ್ ಬಳಸೊಂದಿತ್ತಿನ ನಕಲಿ ಗಡ್ಡ ಸಲೀಂ ಕುಮಾರ್ ನ ಚರ್ಮದ ಬಣ್ಣೊಗು ಸರಿ ಆಯಿಜಿ. ಅಂಚಾದ್, ಆರ್ ತನ್ನ ಸ್ವಂತ ದುಡ್ಡುನ್ ಖರ್ಚು ಮಲ್ತ್ದ್ ಮುಂಬೈಡ್ದ್ ಒಂಜಿ ಸರಿಹೊಂದುನ ಗಡ್ಡನ್ ಕನತೆರ್. ಚಿತ್ರೀಕರಣ ೭ ನವೆಂಬರ್ ೨೦೧೦ ಡ್ ಸುರು ಆದ್ ಕೇರಳದ ತ್ರಿಶೂರು ಬೊಕ್ಕ ಕೊಜಿಕೋಡ್ ಜಿಲ್ಲೆಲೆಡ್ ನಡತ್ಂಡ್. ಚಿತ್ರದ ಪ್ರಮುಖ ಭಾಗೊಲೆನ್ ತ್ರಿಶೂರುದ ಅಥಾನಿ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಇಲ್ಲಡ್ ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್. ಸುರುತ ಸರ್ತಿ ಕಲಾ ನಿರ್ದೇಶಕೆರಾದ್ ಬೇಲೆ ಮಲ್ತೊಂದಿತ್ತಿನ ಜ್ಯೋತಿಷ್, ಕೇವಲ ₹2 ಲಕ್ಷ ಖರ್ಚುಡ್ ಈ ಸಿನಿಮಾದ ಸೆಟ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಅರಿಫ್ಲೆಕ್ಸ್ ಡಿ-೨೧ ಕ್ಯಾಮೆರಾನ್ ಬಳಸದ್ ಈ ಚಿತ್ರೊನು ಡಿಜಿಟಲ್ ಆದ್ ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್. ಛಾಯಾಗ್ರಾಹಕೆರ್ ಮಧು ಅಂಬತ್ ಸುರುಕು 16 ಎಂಎಂ ಫಿಲ್ಮ್ ನ್ ಬಳಕೆ ಮಲ್ಪೆರೆ ಯೋಜಿಸಯೆರ್, ಅಯಿಟ್ ಖರ್ಚುಲೆನ್ ಮಸ್ತ್ ಕಮ್ಮಿ ಮಲ್ಪೊಲಿ. ಆಂಡಲಾ, ಚಿತ್ರೊಡು ಮಸ್ತ್ ಲಾಂಗ್ ಸೀನ್ ಲೆನ್ ಬಳಕೆ ಮಲ್ಪೊಡು ಪನ್ಪಿನೆಡ್ದಾವರ 35 ಎಂಎಂ ಫಿಲ್ಮ್ ನ್ ಬಳಕೆ ಮಲ್ಪೆರೆ ನಿರ್ಧರಿಸಯೆರ್. ಇಂಚಿನ ಮಲ್ಲ ದೃಶ್ಯೊಲು ಪಾತ್ರೊಲೆಡ್ ಏಕಾಂತತೆದ ಭಾವನೆನ್ ಪರಿಣಾಮಕಾರಿಯಾದ್ ತೋಜಾವುನ ಸಹಾಯ ಮಲ್ಪುಂಡು ಪಂಡ್ದ್ ಆರ್ ಪಂಡೆರ್. ಅಂಬತ್ ಲೈಟಿಂಗ್ ಡಿಸೈನ್ ಡ್ ಲಾ ಪ್ರಯೋಗ ಮಲ್ತೆರ್. ಅಂಚನೆ, ಚಿತ್ರದ ವಿಷಯೊಗು ಬೊಕ್ಕ ದೃಷ್ಟಿಕೋನೊಗು ಸರಿ ಆಯಿನ ಒಂಜಿ ಅನನ್ಯ ಲೈಟಿಂಗ್ ಡಿಸೈನ್ ನ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಚಿತ್ರದ ಒಟ್ಟು ನಿರ್ಮಾಣ ವೆಚ್ಚ ಸುಮಾರ್ ₹1.5 ಕೋಟಿ ಆಂಡ್. ಚಿತ್ರೊನು ಮುಗಿಪಾಯೆರೆ, ಸಲೀಮ್ ಅಹ್ಮದ್ ಟೆಲಿವಿಷನ್ ಸೀರಿಯಲ್ ಲೆಗ್ ಸ್ಕ್ರಿಪ್ಟ್ ಬರೆಯೆರೆ ಬೊಕ್ಕ ಸಹಾಯಕ ನಿರ್ದೇಶಕೆರಾದ್ ಬೇಲೆ ಮಲ್ಪೆರೆ ವರ್ಷೊಡ್ದಿಂಚಿ ಉಳಿತಾಯ ಮಲ್ತಿನ ಮಾತಾ ದುಡ್ಡುನ್ ಖರ್ಚು ಮಲ್ಪೊಡಾಂಡ್. ಆಂಡಲಾ, ಆ ದುಡ್ಡು ಸಿನಿಮಾ ಮಲ್ಪೆರೆ ಸಾಕಾಗುಜಿ. ಚಿತ್ರೊಗು ದುಡ್ಡು ಕೊರ್ರೆ ಅತ್ತಂದೆ ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ಏರ್ಲಾ ದುಂಬು ಬರಂದಿನಗ, ಅಹ್ಮದ್ ತನ್ನ ಸ್ನೇಹಿತೆ ಬೊಕ್ಕ ನಿರ್ಮಾಪಕೆ ಅಶ್ರಫ್ ಬೇದಿನ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್. ಅಹ್ಮದ್ ಬರೆತಿನ ಶಾಂಬೂ ಸಿನಿಮಾದ ಸಹ ನಿರ್ಮಾಪಕೆರ್ ಅಶ್ರಫ್ ಬೇದಿ. ಅಹ್ಮದ್ ನ ಒಟ್ಟುಗು ಚಿತ್ರ ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ಆರ್ ತಕ್ಷಣ ಒಪ್ಪಿಗೆ ಮಲ್ತೆರ್. ಉಂದೆಡ್ದ್ ಬೊಕ್ಕ ಚಿತ್ರದ ನಿರ್ಮಾಣೊಗು ವೇಗ ಬತ್ತ್ಂಡ್. ಚಿತ್ರದ ಪ್ರಮುಖ ಫೋಟೋಗ್ರಫಿ ಕೇವಲ 31 ದಿನೊಟು ಮುಗಿಂಡ್. ಚಿತ್ರದ ತಾಂತ್ರಿಕ ಗುಣಮಟ್ಟನ್ ವರ್ಧನೆ ಮಲ್ಪರೆ, ಅಕಾಡೆಮಿ ಪ್ರಶಸ್ತಿ ವಿಜೇತ ಧ್ವನಿ ವಿನ್ಯಾಸಕೆರ್ ರೆಸುಲ್ ಪೂಕುಟ್ಟಿ ಚಿತ್ರದ ಧ್ವನಿನ್ ಪುನರ್ವಿನ್ಯಾಸ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ರೀಮಿಕ್ಸ್ ಮಲ್ತೆರ್. ಲಾಸ್ ಏಂಜಲೀಸ್ ಡ್ ಅಕಾಡೆಮಿ ಸದಸ್ಯೆರೆಗ್ ಅಧಿಕೃತ ಸ್ಕ್ರೀನಿಂಗ್ ಮಲ್ಪುನ ದುಂಬು ಈ ಬೇಲೆನ್ ಮುಗಿತ್ಂಡ್. ಕೆಲವು ಹಿರಿಯ ತಂತ್ರಜ್ಞೆರ್ ಲಾ ಏರ್ಲಾ ಪಾರಿತೋಷಿಕ ಕೊರ್ದು ಚಿತ್ರೊನು ಸುಧಾರಿಸೆರೆ ದುಂಬು ಬತ್ತೆರ್. ಫಿಯಾಸ್ ಟೊಪ್ಪಿಲ್ ಚಿತ್ರದ ಗ್ರಾಫಿಕ್ಸ್ ನ್ ಮಲ್ತೆರ್. == ಥೀಮ್ == ಜಾತಿ ಬೊಕ್ಕ ಧರ್ಮೊನು ಮೀರ್ದ್ ಮಾನವ ಸಂಬಂಧೊಲೆನ ಅನೇಕ ಆಯಾಮೊಲೆನ್ ಒಟ್ಟು ಸೇರಾದ್ ಒಂಜಿ ಸಾರ್ವತ್ರಿಕ ವಿಷಯೊನು ಈ ಸಿನಿಮಾ ಪ್ರಸ್ತುತ ಮಲ್ಪುಂಡು. ಮಾನವ ಮೌಲ್ಯೊಲು ಬೊಕ್ಕ ಬೇತೆ ಬೇತೆ ಸಮುದಾಯೊಲೆನ ನಡುಟು ಸೌಹಾರ್ದತೆ ಚಿತ್ರೊಡು ಪ್ರಮುಖವಾದ್ ತೋಜುಂಡು. ಅಬುಗು ಸಹಾಯ ಮಲ್ಪೆರೆ ದುಂಬು ಬರ್ಪಿನ ಊರುದಕ್ಲೆನ ಪಾತ್ರೊಲೆಡ್ ಈ ಮಾನವೀಯತೆ ತೋಜುಂಡು. ಅವುಲು ಕ್ರೈಸ್ತ ವ್ಯಾಪಾರಿ ಜಾನ್ಸನ್ (ಕಾಲಭವನ ಮಾಣಿ) ಬೊಕ್ಕ ಹಿಂದೂ ಶಾಲೆದ ಗುರು (ನೆದುಮುಡಿ ವೇಣು) ಉಲ್ಲೆರ್. ಒಂಜಿ ಸರಳ ಕಥಾವಸ್ತುದ ಮೂಲಕ, ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬು ಪರಿಸರ ಸಂರಕ್ಷಣೆದ ಸಂದೇಶೊನುಲಾ ಕೊರ್ಪೆರ್. ನರಮಾನ್ಯನ ಪ್ರಕೃತಿ ಬೊಕ್ಕ ಬೇತೆ ಜೀವಿಲೆನೊಟ್ಟುಗು ಉಪ್ಪುನ ಸಮ್ಮಂದೊನು ಇಲ್ಲದ ಎದುರುಡು ಉಪ್ಪುನ ಪೆಲಕಾಯಿದ ಮರ, ಪೆತ್ತದ ಮೂಲಕ ಚಿತ್ರಿಸವೆರ್. ಹಜ್ ಯಾತ್ರೆಗ್ ಪೋಯೆರೆ ದುಡ್ಡು ಬೋಡಾಪುನೆರ್ದಾತ್ರ ಅಬು ಭಾರೀ ಮನಸ್ಸ್ ಡ್ ಗಾಯಿ ಬೊಕ್ಕ ಮರನ್ ಮಾರಾಟ ಮಲ್ಪುವೆ. ಮರನ್ ಕಡ್ತ್ ದ್ ದೀಯೆರೆ ಅಬು ತನ್ನನ್ ತಾನ್ ದೂಷಣೆ ಮಲ್ಪುವೆ. ಮರ ಅಸಂಖ್ಯಾತ ಜೀವಿಲೆನ ಆವಾಸಸ್ಥಾನೊನು ನಾಶ ಮಲ್ತ್ಂಡ್ ಪನ್ಪಿನ ಅರಿವುಡ್ದ್ ಅರೆಗ್ ಭಯಾನಕ ಆಪುಂಡು. ಈ ರೀತಿಡ್ ನರಮಾನಿ ಬೊಕ್ಕ ಪ್ರಕೃತಿದ ನಡುಟು ಉಪ್ಪುನ ಸಂಬಂಧೊನು ಈ ಸಿನಿಮಾ ಸೂಕ್ಷ್ಮವಾದ್ ತೋಜಾವುಂಡು. ಚಿತ್ರದ ಮೂಲ ನಾಟಕೀಯ ಆವೃತ್ತಿಡ್, ಒಸಾಮಾ ಬಿನ್ ಲಾಡೆನ್ ಅರೆನ ಹಜ್ ಯಾತ್ರೆಗ್ ಅಬುಗು ದುಡ್ಡು ಕಡಪುಡುವೆ ಪಂದ್ ಕೆಲವು ಅನುಮಾನಾಸ್ಪದ ಅಂಗಡಿದಾರೆರ್ ಶಂಕೆ ಮಲ್ತಿನ ಒಂಜಿ ದೃಶ್ಯ ಉಂಡು. ಕೆಲವು ವಿಮರ್ಶಕೆರ್ ಈ ಉಲ್ಲೇಖೊನು ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬುನ್ ಆಧುನಿಕ ಮುಸ್ಲಿಂ ಸಮಾಜೊಡು ನಡತಿನ ಒಂಜಿ ಚಿತ್ರೊದ ರೂಪೊಡು ತೋಜಾವುನ ಉದ್ದೇಶಪೂರ್ವಕ ಪ್ರಯತ್ನ ಪಂಡ್ದ್ ಪರಿಗಣಿಸಯೆರ್. ದೈವಿಕ ಶಕ್ತಿಲೆನೊಟ್ಟುಗು ಸಂಪರ್ಕೊಡು ಉಪ್ಪುನ ಒರಿ ಊರುದ ಭವಿಷ್ಯ ಪನ್ಪಿನಾಯೆ ಆದ್ ತೋಜುನ ಉಸ್ತಾದ್ ನ ಪಾತ್ರೊಲಾ ಈ ಚಿತ್ರೊಗು ಮುಸ್ಲಿಂ ಸಾಂಸ್ಕೃತಿಕ ಹಿನ್ನೆಲೆನ್ ಕೊರ್ಪುಂಡು. ಗಮನಾರ್ಹವಾದ್, ಅಡಮಿಂಟೆ ಮಗನ್ ಅಬುಡ್ ಶುದ್ಧವಾದ್ ನಕಾರಾತ್ಮಕ ಪಾತ್ರ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಲಿ ಪನ್ಪಿನ ಒವ್ವೆ ಪಾತ್ರಲಾ ಇಜ್ಜಿ. ಅಬು ಬೊಕ್ಕ ಐಶುಮ್ಮನ ಒಂಜೇ ಮಗೆ ಸತ್ತರ್, ತನ್ನ ಹಿರಿಯ ಅಪ್ಪೆ ಅಮ್ಮೆರೆನ್ ಬುಡ್ದು ಪೋಯಿನೆಡ್ದಾವರ ಕೆಲವು ನಕಾರಾತ್ಮಕ ಗುಣೊಲೆನ್ ತೋಜಾವುನ ಒಂಜೇ ಒಂಜಿ ಪ್ರಮುಖ ಪಾತ್ರ. ಈ ಅಂಶ ಭಾರತೊಡು ನಿರಾಶ್ರಿತೆರ್ ಬೊಕ್ಕ ನಿರ್ಲಕ್ಷಿತ ಹಿರಿಯ ನಾಗರಿಕೆರ್ ಎದುರಿಸೊಂದಿಪ್ಪುನ ಸಮಸ್ಯೆಲೆನ್ ಪ್ರತಿಬಿಂಬಿಸವುಂಡು. ಈ ಕಾರಣೊಗು, ಈ ಸಿನಿಮಾನ್ ಸಾಮಾಜಿಕ ಕಾಳಜಿಲೆನ್ ತೋಜಾವುನ ಸಿನಿಮಾ ಪಂಡ್ದ್ ಲಾ ಗುರುತಿಸವೊಂಡುಂಡು. ಜಾಗತಿಕೀಕರಣೊಲಾ ಈ ಚಿತ್ರೊದ ಪ್ರಮುಖ ವಿಷಯೊಲೆಡ್ ಒಂಜಿ. ಸಮಕಾಲೀನ ಕೇರಳದ ಮಿತ್ತ್ ಜಾಗತಿಕೀಕರಣದ ಪ್ರಭಾವೊನು ಈ ಸಿನಿಮಾ ತೋಜಾವುಂಡು. ಕೇರಳೊಡ್ದ್ ಮಸ್ತ್ ಪುರುಷೆರ್ ಉದ್ಯೋಗೊಗು ದುಬೈ ಅತ್ತ್ಂಡ ಬೇತೆ ಗಲ್ಫ್ ದೇಶೊಲೆಗ್ ವಲಸೆ ಪೋಯೆರ್. ಅಕ್ಲೆನ ಕುಟುಮೊಗು ಕಾಸ್ ಕಡಪುಡುಂಡಲಾ, ಅಕ್ಲೆನ ದೈಹಿಕ ಅನುಪಸ್ಥಿತಿಡ್ದ್ ಕೇರಳದ ಸಾಮಾಜಿಕ ಬದ್‌ಕ್‌ಡ್ ಮಹತ್ವೊದ ಬದಲಾವಣೆಲು ಬತ್ತ್‌ಂಡ್. ಕೆಲವು ವಿಮರ್ಶಕೆರ್ ಕತೆತ ಪಾತ್ರೊಲು ಬೊಕ್ಕ ಸೆಟ್ಟಿಂಗ್ ನ್ ಮಸ್ತ್ ಆದರ್ಶೀಕರಣ ಮಲ್ತೆರ್ ಪಂದ್ ಅನಿಸಿಕೆ ಮಲ್ತೆರ್. ಪುದರ್ ಪೋಯಿನ ವಿಮರ್ಶಕೆರ್ ಎಸ್. ಆನಂದನ್ ಈ ಚಿತ್ರದ ಸೆಟ್ಟಿಂಗ್ ನ್ ಮಸ್ತ್ ಆದರ್ಶೀಕರಣ ಮಲ್ತೆರ್ ಪಂಡ್ದ್ ಗಮನಿಸಯೆರ್. ಒಂಜಿ ಬಿಜಿ ಊರುದ ಎದುರುಡು ಇತ್ತ್ಂಡಲಾ, ಚಿತ್ರೊಡು ಉಪ್ಪುನ ಊರು ಮಸ್ತ್ ಶಾಂತಿಯುತವಾದ್ ಉಪ್ಪುನವು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಅಲ್ಪದ ಜನಕುಲು ಒಂಜೊಂಜಿ ಸೌಹಾರ್ದೊಡು ಜೀವನ ಮಲ್ಪುನವು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಪ್ರಕಾರ, 1980 ದಶಕೊಡು ಗ್ರಾಮೀಣ ಕೇರಳೊನು ನೆನಪು ಮಲ್ಪುನ ಒಂಜಿ ಸಮಾಜ-ಎಡ್ಡೆ ಜನಕುಲು ಬೊಕ್ಕ ನಿಗೂಢ ವ್ಯಕ್ತಿಲೆಡ್ದ್ ತುಂಬಿನ ಪರಿಸರ-ಇತ್ತೆದ ಕಾಲೊಡು ವಾಸ್ತವಿಕವಾದ್ ತೋಜುಜಿ. ಆಂಡಲಾ, ಈ ಪಾತ್ರೊಲು ಆದರ್ಶವಾದಿತ್ತ್ಂಡಲಾ, ಅಂಚನೆ ಜನಕುಲು ನಿಜ ಜೀವನೊಡು ಉಪ್ಪುವೆರ್ ಪಂದ್ ಸಲೀಮ್ ಅಹ್ಮದ್ ಕಾಮೆಂಟ್ ಮಲ್ತೆರ್. ಅಬುನಲೆಕನೆ ಒರಿ ಪ್ರಾಮಾಣಿಕ ಬೊಕ್ಕ ಬೇತೆಕ್ಲೆನ ಮಿತ್ತ್ ದಯೆ ತೋಜಾವುನ ಒರಿ ಸಮಾಜೊಡ್ದ್ ದಯೆನ್ ಬದಲ್ ಗ್ ದೆತೊನುವೆರ್ ಪನ್ಪಿನ ನಂಬಿಕೆನ್ ಆರ್ ತೋಜಾವೆರ್. == ಸಂಗೀತ == ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಚಿತ್ರದ ಮೂಲ ಹಿನ್ನೆಲೆ ಸಂಗೀತ ಸಂಯೋಜನೆ ಐಸಾಕ್ ಥಾಮಸ್ ಕೊಟ್ಟುಪಳ್ಳಿ. ಚಿತ್ರೊದ ಕತೆ ಬೊಕ್ಕ ಭಾವನೆಲು ಒಂಜಿ ಪ್ರದೇಶೊಗು ಸೀಮಿತವಾತ್ ಇಜ್ಜಿ, ಅವು ಸಾರ್ವತ್ರಿಕವಾದುಪ್ಪುನೆರ್ದಾತ್ರ ಸಂಗೀತೊನು ಒಂಜಿ ನಿರ್ದಿಷ್ಟ ಪ್ರಾದೇಶಿಕ ಶೈಲಿಗ್ ಸೀಮಿತ ಮಲ್ಪೆರೆ ಆರ್ ಬಯಕೆ ಮಲ್ತೊಂದಿಜ್ಜೆರ್.<ref>{{cite web|url=https://www.rediff.com/movies/slide-show/slide-show-1-south-interview-with-isaac-thomas-kottukapally/20110606.htm|title=Films can do without background music|author=Shobha Warrier|date=6 June 2011|publisher=[[Rediff]]|access-date=8 June 2011}}</ref><ref>{{cite news|url=http://mangalam.com/index.php?page=detail&nid=430323&lang=malayalam|title=വഴിമാറിപ്പോയ പഥികന്‍|language=ml|author=Ramesh Puthiyamathom|work=[[Mangalam]]|date=29 May 2011|access-date=8 June 2011}}</ref> ಈ ಚಿತ್ರ ಹಜ್ ಯಾತ್ರೆದ ಸುತ್ತ ಸುತ್ತುವರಿದ್ ಉಪ್ಪುನೆರ್ದಾತ್ರ, ಸಂಗೀತ ಅರೇಬಿಕ್ ಸಂಗೀತ ಮಾಪಕೊಲೆನ್ ಬಳಕೆ ಮಲ್ಪುಂಡು. ಆಂಡಲಾ, ಹಜ್ ಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ವಿಷಯೊಲು ಪರದೆಡ್ ಬರ್ಪಿನ ಮುಟ್ಟ ಅವೆನ್ ಕಮ್ಮಿಡ್ ಬಳಕೆ ಮಲ್ಪುವೆರ್. ಸಂಗೀತೊಡು ಔಡ್, ಮಂಡೊಲಿನ್, ಸಂತೂರ್ ಬೊಕ್ಕ ಸರೋಡ್ ಇಂಚಿನ ವಾದ್ಯೊಲೆನ್ ಬಳಕೆ ಮಲ್ಪುವೆರ್. ಸಂತೂರ್ ಬೊಕ್ಕ ಸರೋಡ್ ದ ಶಬ್ದೊಗು ಔಡ್ ದ ಶಬ್ದೊಗು ಕೆಲವು ಸಾಮ್ಯತೆ ಉಂಡು. ತಬ್ಲಾ, ಕೊಳಲು, ಉತ್ತರ ಕೇರಳದ ಸಾಂಪ್ರದಾಯಿಕ ಡೊಲ್ಲುಲು ಬೊಕ್ಕ ಆಫ್ರಿಕಾದ ಡೊಲ್ಲುಲುಲಾ ಸಂಗೀತೊಡು ಉಪಯೋಗ ಆಪುಂಡು. ಮಲಯಾಳಂ ಚಿತ್ರೊಲೆಡ್ ಸಾಮಾನ್ಯವಾದ್ ತೋಜುನ ಸಂಗೀತದ ಶೈಲಿಗ್ ವಿರುದ್ಧವಾದ್, ಈ ಚಿತ್ರೊಡು ಕನಿಷ್ಠ ಸಂಗೀತೊನು ಬಳಕೆ ಮಲ್ತೆರ್. ಸಂಗೀತ ಗಳಸಂದೆ ಮಸ್ತ್ ದೃಶ್ಯೊಲೆಡ್ ಮೌನೊಗು ಬಾರೀ ಮಹತ್ವ ಕೊರ್ತೆರ್ ಕೊಟ್ಟುಪಳ್ಳಿ. ಮೌನೊದ ದುಂಬು ಬೊಕ್ಕ ನಂತರ ಆರೋಹಣ ಬೊಕ್ಕ ಅವರೋಹಣ ಸ್ವರೊಲೆನ್ ಬಳಕೆ ಮಲ್ತ್ ದ್ ದೃಶ್ಯೊಲೆನ ಭಾವನೆನ್ ವರ್ಧನೆ ಮಲ್ತೆರ್.<ref>{{cite web|url=http://www.manoramaonline.com/cgi-bin/MMOnline.dll/portal/ep/musicAlbumDetails.do?albumOid=2257839&type=FILM&BV_ID=@@@|title=Adaminte Makan Abu|work=[[Malayala Manorama]]|access-date=30 May 2011|archive-url=https://web.archive.org/web/20120303101427/http://www.manoramaonline.com/cgi-bin/MMOnline.dll/portal/ep/musicAlbumDetails.do?albumOid=2257839&type=FILM&BV_ID=@@@|archive-date=3 March 2012|url-status=dead}}</ref> == ಪದ್ಯೊಲು ಬೊಕ್ಕ ಧ್ವನಿಪಥ == ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಚಿತ್ರದ ಧ್ವನಿಪಥೊಡು ಉಪ್ಪುನ ಪಾಡ್ದನೊಲೆನ್ ರಮೇಶ್ ನಾರಾಯಣ್ ರಚನೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಸಾಹಿತ್ಯೊನು ರಫೀಕ್ ಅಹ್ಮದ್ ಬರೆತೆರ್.<ref>{{cite web|url=http://www.bfi.org.uk/lff/calendar/20111013 |title=Programme for Thursday 13 October |publisher=[[British Film Institute]] |date=13 October 2011 |access-date=9 November 2011 |url-status=dead |archive-url=https://web.archive.org/web/20111105003119/http://www.bfi.org.uk/lff/calendar/20111013 |archive-date=5 November 2011 }}</ref> '''ಧ್ವನಿಪಥೊನು ಮನೋರಮಾ ಸಂಗೀತ ಬಿಡುಗಡೆ ಮಲ್ತ್ಂಡ್.''' '''ಈ ಗಾಯಕೆರ್ ಈ ಸಿನಿಮಾದ ಪದ್ಯೊಲೆಗ್ ಅಕ್ಲೆನ ಸ್ವರೊನು ಕೊರ್ತೆರ್:''' * ಶಂಕರ ಮಹಾದೇವನ್ * ರಮೇಶ್ ನಾರಾಯಣ್ * ಹರಿಹರನ್ * ಸುಜಾತಾ * ಮಧುಶ್ರೀ ನಾರಾಯಣ್ * ಶ್ರೀನಿವಾಸ್ ಚಿತ್ರೊಡು ಉಪ್ಪುನ ಪಾಡ್ದನೊಲು ಸಾಮಾನ್ಯವಾದ್ ಎಡ್ಡೆ ರೀತಿಡ್ ಸ್ವೀಕಾರ ಆಂಡ್. ಸಿಟಿ ಜರ್ನಲ್ ದ ಟಿ. ಸುಧೀಶ್ ಪಾಡ್ದನೊಲೆನ್ ಅರ್ಥಪೂರ್ಣ ಬೊಕ್ಕ ಮನಸ್ಸ್ ಗ್ ತಾಗುನಂಚಿನವು ಪಂಡ್ದ್ ಮೆಚ್ಚಿಯೆರ್.<ref>{{cite news|url=http://twitchfilm.com/news/2011/10/this-years-saiff-includes-indias-oscar-entry-abu-son-of-adam.php |title=This Year's SAIFF Includes India's Oscar Entry, ABU, SON OF ADAM |author=Ben Umstead |publisher=[[Twitch Film]] |date=20 October 2011 |access-date=26 October 2011 |url-status=dead |archive-url=https://web.archive.org/web/20111025194124/http://twitchfilm.com/news/2011/10/this-years-saiff-includes-indias-oscar-entry-abu-son-of-adam.php |archive-date=25 October 2011 }}</ref><ref>[https://web.archive.org/web/20111208143946/http://www.saiff.org/2011/index.php?p=openingnight "Opening Night"]. South Asian International Film Festival. 9 November 2011. Retrieved 9 November 2011.</ref> ಡೆಕ್ಕನ್ ಕ್ರಾನಿಕಲ್ ದ ಕೀರ್ತಿ ರಾಮಚಂದ್ರನ್ ಚಿತ್ರದ ಸಂಗೀತ ಅತ್ಯುತ್ತಮ ಆಂಡ್ ಚಿತ್ರದ ವಾತಾವರಣೊಗು ಸರಿಯಾದ್ ಸರಿ ಆಂಡ್ ಪಂದ್ ಅನುಭವಿಸಯೆರ್. ಬೇತೆ ಗಾಯಕೆರ್ ಪೊರ್ಲುದ ಸಂಗೀತ ಕೊರ್ನಗ, "ಮಕ್ಕಾ ಮದೀನಾ" ಪನ್ಪಿನ ಪಾಡ್ದನ ಚಿತ್ರದ ಮುಖ್ಯ ವಿಷಯೊನು ಅತ್ಯಂತ ಪರಿಣಾಮಕಾರಿಯಾದ್ ವ್ಯಕ್ತಪಡಿಸಾಯಿನವು ಪಂಡ್ದ್ ಆರ್ ಪಂಡೆರ್.<ref name="IFFI award">T. Ramavarman. (3 December 2011). [https://web.archive.org/web/20130527060444/http://articles.timesofindia.indiatimes.com/2011-12-03/kochi/30472071_1_panorama-section-malayalam-film-indian-panorama "Special Jury Award for Adaminte Makan Abu at IFFI"]. ''[[The Times of India]]''. Retrieved 3 December 2011.</ref> == ಬಿಡುಗಡೆ == '''ಉತ್ಸವದ ಪ್ರದರ್ಶನೊಲು''' ಬಿಡುಗಡೆ ಆಯಿ ಬೊಕ್ಕ, ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಭಾರತ ಬೊಕ್ಕ ವಿದೇಶೊಡು ಮಸ್ತ್ ಮಲ್ಲ ಚಿತ್ರೋತ್ಸವೊಲೆಡ್ ಪ್ರದರ್ಶನ ಆಂಡ್.<ref>{{Deprecated archive|url=https://archive.today/20120729230416/http://www.fajrfestival.ir/30th/en/index.php?option=com_content&view=article&id=182:films-lineup-for-cinema-of-salvation&catid=36:world-panorama&Itemid=59|title="Films Lineup for Cinema of Salvation"}}. Fajrfestival.ir. Retrieved 23 March 2012.</ref> ಈ ಸಿನಿಮಾ 2011 ಅಕ್ಟೋಬರ್ 13 ತಾರೀಕ್ ಗ್ 55ನೇ ಬಿಎಫ್ಐ ಲಂಡನ್ ಫಿಲ್ಮ್ ಫೆಸ್ಟಿವಲ್ ಡ್ ಯುರೋಪಿಯನ್ ಪ್ರೀಮಿಯರ್ ಆಂಡ್, ಅಲ್ಪ ಉಂದು "ವರ್ಲ್ಡ್ ಸಿನಿಮಾ" ವಿಭಾಗೊಡು ಪ್ರದರ್ಶನ ಆಂಡ್. ಉಂದೆತ ಭಾರತ ಪ್ರಥಮ ಪ್ರದರ್ಶನ MAMI ಮುಂಬೈ ಚಿತ್ರೋತ್ಸವೊಡು ೧೮ ಅಕ್ಟೋಬರ್ ೨೦೧೧ಡ್ ನಡತ್ಂಡ್, ಅಲ್ಪ ಉಂದು "ಭಾರತೀಯ ಚೌಕಟ್ಟ್" ವಿಭಾಗೊಡು ಪ್ರದರ್ಶನ ಆಂಡ್. ಫಸ್ಟ್ಪೋಸ್ಟ್ ಡಾಟ್ ಕಾಮ್ ವಿಮರ್ಶಕೆರ್ ವಿಕ್ರಮ್ ಫುಕನ್ ಲಾ ಮುಂಬೈ ಚಿತ್ರೋತ್ಸವೊಡು ಪ್ರದರ್ಶನ ಆಯಿನ ಪತ್ತ್ ಎಡ್ಡೆ ಚಿತ್ರೊಲೆಡ್ ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬುನ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್.<ref name=Kazan1>Priya Sreekumar. (15 September 2012). [https://web.archive.org/web/20120915091813/http://www.deccanchronicle.com/channels/showbiz/mollywood/salim-ahmed-bags-two-awards-international-fest-011 "Salim Ahmed bags two awards at international fest"] {{webarchive|url=https://web.archive.org/web/20120915091813/http://www.deccanchronicle.com/channels/showbiz/mollywood/salim-ahmed-bags-two-awards-international-fest-011 |date=15 September 2012 }}. ''[[Deccan Chronicle]]''. Retrieved 21 September 2012.</ref><ref name=Kazan2>[https://web.archive.org/web/20121004082713/http://en.kazan-mfmk.com/archives/354 "The winners of the Eighth Kazan International Muslim Film Festival"] {{Webarchive|url=https://web.archive.org/web/20121004082713/http://en.kazan-mfmk.com/archives/354 |date=4 October 2012 }}. [[Kazan International Festival of Muslim Cinema]]. 11 September 2012. Retrieved 21 September 2012.</ref> ಈ ಚಿತ್ರ 9 ನವೆಂಬರ್ 2011 ಗ್ **8ನೇ ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಅಂತರರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವೊಡು (SAIFF)** ಡ್ ಉತ್ತರ ಅಮೆರಿಕೊಡು ಪ್ರದರ್ಶನ ಆಂಡ್. ಈ ಚಿತ್ರೊನು ಉತ್ಸವೊದ ಸುರುತ ರಾತ್ರೆಡ್ ಪ್ರದರ್ಶನ ಮಲ್ತೆರ್. '''ಅಂತರರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವೊಲೆಡ್ ಪ್ರದರ್ಶನ''' **ಅಂತರರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವ ಆಫ್ ಭಾರತ (ಐಎಫ್ ಎಫ್ ಐ)** ದ ಭಾರತೀಯ ಪನೋರಮಾ ವಿಭಾಗೊಡು ಪ್ರದರ್ಶನ ಆಯೆರೆ ಆಯ್ಕೆ ಆಯಿನ ಏಳು ಮಲಯಾಳಂ ಚಿತ್ರೊಲೆಡ್ ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಒಂಜಿ. ಈ ಚಿತ್ರ ಅತ್ಯುತ್ತಮ ಚಿತ್ರದ ವಿಶೇಷ ತೀರ್ಪುಗಾರರ ಪ್ರಶಸ್ತಿ ಗೆಂದಿದೆ.<ref>[http://chicagosouthasianfilmfestiv2012.sched.org/event/90f69beeed433c731b006d4cd4d0ea51#.UFyoTKNTGSo "Chicago South Asian Film Festival"]. 2012. Retrieved 21 September 2012.</ref> ಧರ್ಮ, ಪ್ರಕೃತಿ ಬೊಕ್ಕ ಮಾನವೀಯತೆದ ಆದರ್ಶೊಲೆನ್ ಶುದ್ಧ ದೃಷ್ಟಿಕೋನೊಡ್ದ್ ತೋಜಾವುನೆರ್ದಾತ್ರ ಈ ಚಿತ್ರೊನು ಈ ಪ್ರಶಸ್ತಿಗ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್ ಪಂದ್ ಐಎಫ್ ಎಫ್ ಐ ತೀರ್ಪುಗಾರೆರೆನ ಅಧ್ಯಕ್ಷರಾದ್ ಇತ್ತಿನ ಖ್ಯಾತ ಮಲಯಾಳಂ ಚಿತ್ರ ನಿರ್ದೇಶಕೆರ್ ಅದೂರ್ ಗೋಪಾಲಕೃಷ್ಣನ್ ಪನ್ಪೆರ್.<ref>[https://web.archive.org/web/20211128014649/https://www.mpac.org/events/mpac-hosts-screening-of-abu-son-of-adam-on-411.php "MPAC to Host Screening of ‘Abu, Son of Adam’ on 4/11"]. Muslim Public Affairs Council. 11 April 2012. Retrieved 21 September 2012.</ref> **ಕೇರಳದ 16ನೇ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವ (ಐಎಫ್ ಎಫ್ ಕೆ)** ದ ಅಂತರಾಷ್ಟ್ರೀಯ ಸ್ಪರ್ಧೆ ವಿಭಾಗೊಗು ಆಯ್ಕೆ ಆಯಿನ ಒಂಜೇ ಒಂಜಿ ಮಲಯಾಳಂ ಚಿತ್ರ ಈ ಚಿತ್ರ. ಆಂಡ ಭಾರತೊಡು ನಡಪುನ ಬೇತೆ ಒಂಜಿ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವೊದ ಸ್ಪರ್ಧೆದ ವಿಭಾಗೊಡು ಪಾಲ್ ಪಡೆಯಿನ ಚಿತ್ರೊನು ಐಎಫ್ ಎಫ್ ಕೆ ಸ್ಪರ್ಧೆದ ವಿಭಾಗೊಗು ಸೇರಾವರೆ ಸಾಧ್ಯ ಇಜ್ಜಿ ಪನ್ಪಿನ ನಿಯಮೊಡ್ದಾವರ ಚಿತ್ರೊನು ಸ್ಪರ್ಧೆಡ್ದ್ ಪಿರ ದೆತ್ತ್ ದ್ ದೀಯೆರ್.<ref>Akbar Ponnani. 22 February 2012. [https://web.archive.org/web/20130608162118/http://articles.timesofindia.indiatimes.com/2012-02-22/middle-east-news/31086471_1_asian-film-festival-adaminte-makan-abu-jeddah "'Abu, Son of Adam' screened in ACGC film festival in Jeddah"]. ''[[The Times of India]]''. Retrieved 21 September 2012.</ref><ref>[http://www.coastaldigest.com/index.php?option=com_content&task=view&id=36679 "‘Adaminte Makan Abu’ screened in ACGC"]{{dead link|date=October 2016 |bot=InternetArchiveBot |fix-attempted=yes }}. Coastaldigest.com. 22 February 2012. Retrieved 27 February 2012.</ref> ಆಂಡಲಾ, ಐಎಫ್ ಎಫ್ ಕೆ ದ “ಮಲಯಾಳಂ ಸಿನಿಮಾ ಟುಡೇ” ವಿಭಾಗೊಡು ಚಿತ್ರ ಪ್ರದರ್ಶನ ಆಂಡ್. ಜನವರಿ ೨೦೧೨ಡ್ ೨೩ನೇ ಪಾಮ್ ಸ್ಪ್ರಿಂಗ್ಸ್ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವೊಡುಲಾ ಈ ಚಿತ್ರೊನು ಪ್ರದರ್ಶಿಸಯೆರ್. ಈ ಉತ್ಸವೊನು ಅಕಾಡೆಮಿ ಪ್ರಶಸ್ತಿಲೆಗ್ ಕಾರಣವಾದ್ ಪ್ರಮುಖ ಉತ್ಸವೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಪರಿಗಣಿಸವೆರ್. ಅತ್ತಂದೆ, 30ನೇ ಫಜರ್ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವೊದ “ಸಿನಿಮಾ ಆಫ್ ಸಾಲ್ವೇಶನ್ ಸ್ಪರ್ಧೆ” ವಿಭಾಗೊಡು ಈ ಚಿತ್ರೊನು ಪ್ರದರ್ಶಿಸಯೆರ್. ''' ಅಂತರಾಷ್ಟ್ರೀಯ ಪ್ರಶಸ್ತಿಲು''' * ಸಲೀಮ್ ಕುಮಾರ್ ಚಿತ್ರೊಡು ಅಬು ಪಾತ್ರೊಡು ನಟನೆ ಮಲ್ತಿನ 11ನೇ ಇಮ್ಯಾಜಿನ್ ಇಂಡಿಯಾ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವೊಡು ಅತ್ಯುತ್ತಮ ನಟ ಪ್ರಶಸ್ತಿನ್ ಗೆಂದಿಯೆರ್. * 2012 ಡ್ **ಸಿಯಾಟಲ್ ಇಂಟರ್ನ್ಯಾಷನಲ್ ಫಿಲ್ಮ್ ಫೆಸ್ಟಿವಲ್ (SIFF)** ಡ್ ಪ್ರದರ್ಶನ ಆಯಿನ ನಾಲ್ ಭಾರತೀಯ ಸಿನಿಮಾಲೆಡ್ ಒಂಜಿ ಆದಿಮಿಂಟೆ ಮಗನ್ ಅಬು. * ರಷ್ಯಾಡ್ ನಡತಿನ 8ನೇ ಕಜಾನ್ ಅಂತರಾಷ್ಟ್ರೀಯ ಮುಸ್ಲಿಂ ಸಿನಿಮಾ ಉತ್ಸವೊಡು, ಈ ಚಿತ್ರ ಗೆಂದಿನವು: * ಅತ್ಯುತ್ತಮ ಲಿಪಿ ಪ್ರಶಸ್ತಿ * ರಷ್ಯಾದ ಚಿತ್ರ ವಿಮರ್ಶಕೆರ್ ಬೊಕ್ಕ ಚಿತ್ರ ವಿದ್ವಾಂಸೆರೆನ ಗಿಲ್ಡ್ ಪ್ರಶಸ್ತಿಲು.<ref>{{cite news|first=Jason |last=Ferguson |date=3 October 2012 |url=http://www.orlandoweekly.com/film/18th-annual-south-asian-film-festival-1.1382532 |archive-url=https://archive.today/20130130230137/http://www.orlandoweekly.com/film/18th-annual-south-asian-film-festival-1.1382532 |url-status=dead |archive-date=30 January 2013 |title=18th Annual South Asian Film Festival |work=[[Orlando Weekly]] |access-date=24 October 2012 }}</ref> '''ಬೇತೆ ಚಿತ್ರೋತ್ಸವೊಲು''' '''ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬುನ್ ಈ ಕೆಳಕಂಡ ಪ್ರಮುಖ ಚಿತ್ರೋತ್ಸವೊಲೆಡ್ ಅಧಿಕೃತವಾದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್:''' * 5ನೇ ಇಂಡೋ-ಜರ್ಮನ್ ಚಲನಚಿತ್ರೋತ್ಸವ * 17ನೇ ಕೋಲ್ಕತ್ತಾ ಚಿತ್ರೋತ್ಸವ * 9ನೇ ಚೆನ್ನೈ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವ * 4ನೇ ಬೆಂಗಳೂರು ಅಂತರಾಷ್ಟ್ರೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ * 10ನೇ ಪುಣೆ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಿತ್ರೋತ್ಸವ * 35ನೇ ಪೋರ್ಟ್ಲ್ಯಾಂಡ್ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ * ಚಿಕಾಗೊ ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಚಿತ್ರೋತ್ಸವ * 4ನೇ ಸಿನಿ ಎಎಸ್ಎ ಗುವಾಹಟಿ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ * 10ನೇ ಲಾಸ್ ಏಂಜಲೀಸ್ ಭಾರತೀಯ ಚಿತ್ರೋತ್ಸವ * 5ನೇ ಏಷ್ಯಾದ ಕಾನ್ಸಲ್ಸ್ ಜನರಲ್ ಕ್ಲಬ್ (ಎಸಿಜಿಸಿ) ಚಿತ್ರೋತ್ಸವ, ಜೆಡ್ಡಾ * 14ನೇ ಲಂಡನ್ ಏಷ್ಯನ್ ಫಿಲ್ಮ್ ಫೆಸ್ಟಿವಲ್ * 1ನೇ ವಾಷಿಂಗ್ಟನ್ ಡಿಸಿ ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಚಲನಚಿತ್ರೋತ್ಸವ * ೧ನೇ ಲಡಾಖ್ ಅಂತರಾಷ್ಟ್ರೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ * 5ನೇ ಸ್ಟಟ್ಗಾರ್ಟ್ ಭಾರತೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ, ಜರ್ಮನಿ * 2ನೇ ದಿ ಹೇಗ್ ಇಂಡಿಯನ್ ಫಿಲ್ಮ್ ಫೆಸ್ಟಿವಲ್, ನೆದರ್ಲ್ಯಾಂಡ್ಸ್ * ಭಾರತೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ, ಪುದುಚೇರಿ * ಬಾಲಿವುಡ್ ದಾಂತೆ: 18ನೇ ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಚಲನಚಿತ್ರೋತ್ಸವ, ಒರ್ಲ್ಯಾಂಡೊ * 12ನೇ ರಿವರ್ ಟು ರಿವರ್ ಫ್ಲೋರೆನ್ಸ್ ಭಾರತೀಯ ಚಲನಚಿತ್ರೋತ್ಸವ ಈ ರೀತಿಡ್, ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಮಲಯಾಳಂ ಚಿತ್ರರಂಗೊಡು ಮಾತ್ರ ಅತ್ತ್ ಭಾರತೊದ ಬೇತೆ ಬೇತೆ ಭಾಗೊಲೆಡ್ ಬೊಕ್ಕ ಅಂತರರಾಷ್ಟ್ರೀಯ ಮಟ್ಟೊಡುಲಾ ಹಿಟ್ ಆಯೆರ್, ಗಮನಾರ್ಹ ಚಿತ್ರೋತ್ಸವೊಲೆಡ್ ಪ್ರಶಂಸೆ ಬೊಕ್ಕ ಪ್ರಶಸ್ತಿಲೆನ್ ಗೆಂದಿಯೆರ್. == ನಾಟಕೀಯ ಬಿಡುಗಡೆ == ಶೂಟಿಂಗ್ ಬೊಕ್ಕ ಎಡಿಟಿಂಗ್ ಮುಗಿ ಬೊಕ್ಕ ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬುನ್ ಮುಂಬೈಡ್ ಸೆನ್ಸರ್ ಬೋರ್ಡ್ ಗ್ ಕಡಪುಡುವೆರ್. ಅಲ್ಪಡ್ದ್, ಚಿತ್ರದ ಪ್ರತಿಲೆನ್ ರಾಷ್ಟ್ರೀಯ ಬೊಕ್ಕ ರಾಜ್ಯ ಚಿತ್ರ ಪ್ರಶಸ್ತಿ ತೀರ್ಪುಗಾರರ ಸಮಿತಿಲೆಗ್ ನೇರವಾದ್ ಕಡಪುಡುವೆರ್.<ref>{{cite web|url=http://entertainment.oneindia.in/malayalam/news/2011/mammootty-delay-bombay-march-12-salimkumar-220611-aid0062.html|title=Mammootty delays Bombay March 12 for Salim Kumar|publisher=[[Oneindia.in]]|date=22 June 2011|access-date=29 June 2011|archive-date=22 October 2012|archive-url=https://web.archive.org/web/20121022150542/http://entertainment.oneindia.in/malayalam/news/2011/mammootty-delay-bombay-march-12-salimkumar-220611-aid0062.html|url-status=dead}}</ref><ref>{{cite news|url=http://www.madhyamam.com/news/91383/110623 |title=ആദാമിനായി വഴിമാറി ബോംബെ മാര്‍ച്ച് 12 |language=ml |work=[[Madhyamam]] |date=23 June 2011 |access-date=2 July 2011 |url-status=dead |archive-url=https://web.archive.org/web/20110628010440/http://madhyamam.com/news/91383/110623 |archive-date=28 June 2011 }}</ref> ಮೇ ಮುಗಿಲ್ ಡ್, ರಾಷ್ಟ್ರೀಯ ಚಿತ್ರ ಪ್ರಶಸ್ತಿಗ್ ಸಲ್ಲಿಸಾಯಿನ ದಾಖಲೆಲೆಡ್ ಸಹ ನಿರ್ಮಾಪಕೆ ಪಂಡ್ದ್ ತನ್ನ ಪುದರ್ ನ್ ತೆರಿಪಾದ್ ಇಜ್ಜಿ ಪಂದ್ ಚಿತ್ರದ ಸಹ ನಿರ್ಮಾಪಕೆ ಅಶ್ರಫ್ ಬೇದಿ ನ್ಯಾಯಾಲಯೊಗು ಸಂಪರ್ಕ ಮಲ್ತೆರ್. ಈ ಚಿತ್ರೊನು ಸಲೀಮ್ ಅಹ್ಮದ್ ಮಾತ್ರ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ ಪಂದ್ ದಾಖಲೆಲೆಡ್ ಉಂಡು. ಈ ಕಾರಣೊಗು, ನ್ಯಾಯಾಲಯ ಜೂನ್ 10 ಮುಟ್ಟ ಚಿತ್ರದ ಬಿಡುಗಡೆ ಬೊಕ್ಕ ವಿತರಣೆನ್ ತಡೆತ್ಂಡ್. ಸಲೀಮ್ ಅಹ್ಮದ್ ನನೊಂಜಿ ಸರ್ತಿ ನ್ಯಾಯಾಲಯೊಗು ಒಪ್ಪಿಗೆ ಕೊರ್ದು ರಾಷ್ಟ್ರೀಯ ಚಿತ್ರ ಪ್ರಶಸ್ತಿಗ್ ಸಲ್ಲಿಸಾಯಿನ ದಾಖಲೆಲೆನ್ ಸರಿ ಮಲ್ತೆರ್. ಸಲೀಮ್ ಅಹ್ಮದ್ ಬೊಕ್ಕ ಅಶ್ರಫ್ ಬೇಡಿ ರಡ್ಡ್ ಜನಲಾ ಸಹ ನಿರ್ಮಾಪಕೆರ್ ಆದ್ ಸೇರ್ದೆರ್. ನನೊಂಜಿ ಸರ್ತಿ ಚಿತ್ರೊನು ಬುಡುಗಡೆ ಮಲ್ಪೆರೆ ಬೊಕ್ಕ ವಿತರಿಸೆರೆ ಅವಕಾಶ ಕೊರ್ತೆರ್. ಪ್ಲೇಹೌಸ್, ಖಾಸ್, ಕಲಾಸಂಘಮ್ ಫಿಲ್ಮ್ಸ್ ಬೊಕ್ಕ ವೈಶಾಕ ಪನ್ಪಿನ ನಾಲ್ ಪ್ರಮುಖ ವಿತರಕೆರ್ ಚಿತ್ರದ ವಿತರಣಾ ಹಕ್ಕುಲೆನ್ ಸ್ವಾಧೀನ ಮಲ್ಪೆರೆ ನಿರ್ಮಾಪಕೆರೆನ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್. ಅಪಗ ಸಲೀಮ್ ಕುಮಾರ್ ತನ್ನ ಪೊಸ ನಿರ್ಮಾಣ ಕಂಪೆನಿ ಲಾಫಿಂಗ್ ವಿಲ್ಲಾ ಮೂಲಕ ಚಿತ್ರೊನು ಸ್ವತಃ ವಿತರಣೆ ಮಲ್ಪೆರೆ ನಿರ್ಧರಿಸಯೆರ್. ಅಲೆನ್ಸ್ ಮೀಡಿಯಾ ರಿಲೀಸ್, ಕಲಾಸಂಘಂ ಫಿಲ್ಮ್ಸ್, ಖಾಸ್ ಬೊಕ್ಕ ಮಂಜುನಾಥನ ಸಹಯೋಗೊಡು ಈ ಚಿತ್ರೊನು ವಿತರಿಸಯೆರ್. ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಚಿತ್ರ ೨೪ ಜೂನ್ ೨೦೧೧ ದಾನಿ ಕೇರಳದ ೭೦ ಚಿತ್ರಮಂದಿರೊಲೆಡ್ ಬಿಡುಗಡೆ ಆಂಡ್. ಬಿಡುಗಡೆ ಆಯೆರೆ ದುಂಬು ಚೆನ್ನೈ, ಎರ್ನಾಕುಲಂ ಬೊಕ್ಕ ಕಣ್ಣೂರುಡ್ ವಿಶೇಷ ಪ್ರದರ್ಶನೊಲೆನ್ ಏರ್ಪಾಡ್ ಮಲ್ತೆರ್. ಮಮ್ಮೂಟ್ಟಿನ ಐತಿಹಾಸಿಕ ಸಿನಿಮಾ ಬೊಂಬಾಯಿ ಮಾರ್ಚ್ 12 ಲಾ ಅವ್ವೇ ದಿನ ಬುಡುಗಡೆ ಆಯೆರೆ ನಿಗದಿ ಆದಿತ್ತುಂಡ್. ಆಂಡ ಆದಿಮಿಂತೆ ಮಗನ್ ಅಬು ಚಿತ್ರೊಗು ಸ್ಪರ್ಧೆ ಮಲ್ಪೆರೆ ಬಲ್ಲಿ ಪನ್ಪಿನ ಮಮ್ಮೂಟ್ಟಿನ ಕೋರಿಕೆಡ್ ಬಾಂಬೆ ಮಾರ್ಚ್ ೧೨ ತಾರೀಕ್ ಗ್ ಬಿಡುಗಡೆ ಆಯೆರೆ ಮುಂದೂಡೊಂಡೆರ್. == ಮುಖ್ಯ ಮಾಧ್ಯಮ == ಸೈನಾ ವಿಡಿಯೋಸ್ ಚಿತ್ರದ ಹೋಮ್ ವಿಡಿಯೋ ಹಕ್ಕುಲೆನ್ ಸ್ವಾಧೀನ ಮಲ್ತೆರ್.<ref>[http://www.keralatv.in/2011/11/adaminte-makan-abu-on-mazhavil-manorama/ "Adaminte Makan Abu Premiering On Mazhavil Manorama"] {{webarchive |url=https://web.archive.org/web/20111113094944/http://www.keralatv.in/2011/11/adaminte-makan-abu-on-mazhavil-manorama/ |date=13 November 2011 }}. Keralatv.in. 6 November 2011. Retrieved 9 November 2011.</ref> ಚಿತ್ರದ ಡಿವಿಡಿಲು ಸೆಪ್ಟೆಂಬರ್ ೨೦೧೧ಡ್ ಬಿಡುಗಡೆ ಆಂಡ್. ಡಿವಿಡಿಲೆಗ್ ಬೇಡಿಕೆ ನಿರೀಕ್ಷೆಡ್ದ್ ಜಾಸ್ತಿ ಇತ್ತ್ಂಡ್. ಈ ಸಿನಿಮಾನ್ ಥಿಯೇಟರ್ ಡ್ದ್ ದುಂಬೇ ಪಿರ ದೆತೊಂಡೆರ್, ಅಂಚಾದ್ ಮಸ್ತ್ ಪ್ರೇಕ್ಷಕೆರ್ ಸಿನಿಮಾನ್ ಥಿಯೇಟರ್ ಡ್ ತೂಯೆರೆ ಸಾಧ್ಯ ಆಯಿಜಿ. ಉಂದು ಡಿವಿಡಿ ಬಿಡುಗಡೆಗ್ ಮಸ್ತ್ ಬೇಡಿಕೆಗ್ ಕಾರಣ ಆಂಡ್ ಪಂದ್ ವರದಿ ಆತ್ಂಡ್. ಚಿತ್ರದ ಟೆಲಿವಿಷನ್ ಪ್ರಥಮ ಪ್ರದರ್ಶನ 7 ನವೆಂಬರ್ 2011 ಗ್ ಮಝವಿಲ್ ಮನೋರಮಾಡ್ ನಡತ್ಂಡ್. ಬಕ್ರಿದ್ ಪರ್ಬೊದ ಪೊರ್ತುಡು ಪ್ರಸಾರ ನಡತ್‌ಂಡ್.<ref>Muhammad Shameem. (Undated). [https://web.archive.org/web/20110930171937/http://www.prabodhanam.net/detail.php?cid=287&tp=1 "ആദാമിന്റെ മകന്‍"] {{webarchive |url=https://web.archive.org/web/20110930171937/http://www.prabodhanam.net/detail.php?cid=287&tp=1 |date=30 September 2011 }}. (in Malayalam). ''Prabhodanam Varika''. Retrieved 9 November 2011.</ref> == ಕೃತಿಚೌರ್ಯದ ಆರೋಪೊಲು == ಚಿತ್ರದ ಕತೆತ ಬಗ್ಗೆ ನನೊಂಜಿ ವಿವಾದ ಉದಿತ್ಂಡ್. ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬುನ ಕತೆನ್ ಆರ್ ಎಂಟು ವರ್ಸೊಗು ದುಂಬು ಮಲ್ತಿನ ಮರುಪ್ಪಾಚಾ ಪನ್ಪಿನ 21 ನಿಮಿಷದ ಕಿರುಚಿತ್ರೊಡ್ದ್ ದೆತೊನುನವು ಪಂದ್ ಚಿತ್ರ ನಿರ್ದೇಶಕೆರ್ ಅಬ್ಬಾಸ್ ಕಲಾಥೋಡ್ ಆರೋಪ ಮಲ್ತೆರ್. ಆರೆನ ಪ್ರಕಾರ ಮರುಪ್ಪಚೆರೆನ ಕತೆ, ಚಿತ್ರಕಥೆ ಬೊಕ್ಕ ಸಂಭಾಷಣೆಲೆನ್ ತಾನ್ನೇ ಬರೆದಿತ್ತೆರ್. ಬಪ್ಪು ಪನ್ಪಿ ಜವನನ ಸುತ್ತ ಸುತ್ತುಂಡು ಮರುಪ್ಪಚನ ಕತೆ. ಬಪ್ಪು ನಿತ್ಯದ ಕನಸುಡ್ದ್ ಒಂತೆ ದುಡ್ಡು ಉಳಿತಾಯ ಮಲ್ತ್ದ್ ಹಜ್ ಯಾತ್ರೆಗ್ ಪೋಯೆರೆ ತಯಾರಾಪುವೆರ್. ಆಂಡ ಆರ್ ಉಳಿತಾಯ ಮಲ್ತಿನ ಪೂರಾ ಮೊತ್ತೊನು ಹಜ್ಜ್ ಗ್ ಪೋಯೆರೆ ಬಯಕೆ ಇತ್ತಿನ ಅರೆನ ನೆರೆಕರೆತಕ್ಲೆಗ್ ಕೊರ್ಪೆರ್. ಅಪಗ ಅಬ್ದುಲ್ಲಾ ಪನ್ಪಿನ ಒರಿ ಕನೊಟು ಒಂಜಿ ದೈವಿಕ ಸ್ವರೊನು ಕೇನುವೆರ್. ಆ ಸ್ವರೊಡು ಆಯಗ್ ದೇವೆರ್ ಮಾತ್ರ ಬಪ್ಪುಲೆನ ಹಜ್ ಯಾತ್ರೆನ್ ಸ್ವೀಕಾರ ಮಲ್ತೆರ್ ಪಂದ್ ತೆರಿಪಾವೆರ್. ಅಬ್ದುಲ್ಲಾ ವಾಪಸ್ ಬನ್ನಗ, ಬಪ್ಪುನ್ ತೂಯೆರೆ ಪಿದಯಿ ಬರ್ಪೆ. ಮಸ್ತ್ ಸರ್ಚ್ ಮಲ್ತಿ ಬೊಕ್ಕ, ಅಕುಲು ರಡ್ಡ್ ಜನಲಾ ತೂಪೆರ್ ಬೊಕ್ಕ ಅಬ್ದುಲ್ಲಾ ಬಪ್ಪುಗು ದಾದ ಆಂಡ್ ಪನ್ಪಿನೆನ್ ಪನ್ಪಿನ ಕತೆ ಮುಗಿಪುಂಡು. ಕಲಾತೋಡ್ ಪ್ರಕಾರ ಆಡಮಿಂತೆ ಮಕನ್ ಅಬು ಮರುಪ್ಪಚ ಸಿನಿಮಾದ ಪಾತ್ರೊಲೆನ್ ಹಿರಿಯ ಮಲ್ತ್‌ದ್ ಕತೆನ್ ವಿಸ್ತಾರ ಮಲ್ತ್‌ದ್ ಮಲ್ತ್‌ದೆರ್. '''ಸಲೀಂ ಕುಮಾರ್ ಆರೋಪೊಲೆಗ್ ಸ್ಪಂದನೆ''' ಸಲೀಂ ಕುಮಾರ್ ಈ ಆರೋಪೊಲೆನ್ ಹಾಸ್ಯಾಸ್ಪದ ಪಂಡ್ದ್ ತಳ್ಳಿಪಾಡ್ದೆರ್. ಅರೆನ ಪ್ರಕಾರ, ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬು ಪನ್ಪಿನ ಚಿತ್ರ ಇತ್ತೆಲಾ ಬಿಡುಗಡೆ ಆಯಿಜಿ. ಸಿನಿಮಾ ತೂವಂದೆ ಕೃತಿಚೌರ್ಯದ ಆರೋಪ ಮಲ್ಪುನವು ಸರಿ ಅತ್ತ್ ಪಂಡೆರ್.<ref name="ICAreview">{{cite news|url=http://www.ica.org.uk/?lid=30460|title=Abu, Son of Adam|author=Jacob Kassay|publisher=Institute of Contemporary Arts|date=12 October – 13 November 2011|access-date=26 October 2011|archive-url=https://web.archive.org/web/20111015104936/http://ica.org.uk/?lid=30460|archive-date=15 October 2011|url-status=usurped|df=dmy-all}}</ref> ಮರುಪ್ಪಚನ ಕತೆ ತೂಯೆ, ಅಯಿಟ್ ಉಪ್ಪುನ ಕತೆ ಕುತ್ತಿಕ್ಕುಪ್ಪಯಂ ಪನ್ಪಿನ ಬೇತೆ ಒಂಜಿ ಮಲಯಾಳ ಸಿನಿಮಾದಲೆಕನೆ ಉಂಡು ಪಂದ್ ಸಲೀಂ ಕುಮಾರ್ ಕಮೆಂಟ್ ಮಲ್ತೆರ್. == ಸೂಫಿ ಕಥೆತ ಸಾಮ್ಯತೆ == ಅಂಚನೆ ಒಂಜಿ ಕತೆ 8ನೇ ಶತಮಾನೊದ ಸೂಫಿ ಸಂತ ಇಬ್ರಾಹಿಂ ಇಬ್ನ್ ಅಧಮ್ ನ ಕತೆಟ್ಲಾ ಉಂಡು. ಆ ಕತೆಟ್, ಒರಿ ಸಾಮಾನುಗಾರೆ ಹಜ್ ಯಾತ್ರೆಗ್ ಪೋಪಿನ ಕನಸ್ ತೂಪೆರ್. ಅಂಚಾದ್, ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬುನ ಕತೆತ ಬಗ್ಗೆ ಚರ್ಚೆಡ್, ಕತೆತ ಕಲ್ಪನೆ ಒಂಜಿ ಕಿರುಚಿತ್ರೊಡ್ದ್ ಮಾತ್ರ ಬತ್ತ್ಂಡ್ ಅತ್ತ್ಂಡ ದುಂಬುದ ಧಾರ್ಮಿಕ ಬೊಕ್ಕ ಜಾನಪದ ಕತೆಕುಲೆಡ್ಲಾ ಇಂಚಿನ ಅಂಶೊಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ಪ್ರಶ್ನೆ ಉದಿತ್ಂಡ್. == ಕ್ರಿಟಿಕಲ್ ರಿಸೆಪ್ಶನ್ == ''“'''ಈ ಸಿನಿಮಾ ಇಸ್ಲಾಂ ಧರ್ಮದ ನಿಜವಾಯಿನ ಸ್ವರೂಪೊನು ಸರ್ವ ಜೀವಿಲೆನ ಒಗ್ಗಟ್ಟ್ ದ ಸಂದೇಶೊನು ಬಾರಿ ಪರಿಣಾಮಕಾರಿಯಾದ್ ತೆರಿಪಾವುಂಡು.'''”'' '''— '''ಭಾರತದ ಗೌರವಾನ್ವಿತ ರಾಷ್ಟ್ರಪತಿ ಪ್ರತಿಭಾ ಪಾಟೀಲ'''''' ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬು ಚಿತ್ರ ವಿಮರ್ಶಕೆರೆಡ ಮಸ್ತ್ ಸಕಾರಾತ್ಮಕ ವಿಮರ್ಶೆಲೆನ್ ಪಡೆಯೊಂಡು. ಮಹಮೂದ್ ಕುರಿಯಾ ಈ ಚಿತ್ರದ ಬಗ್ಗೆ ಪ್ರಕಟವಾದ್ ವಿವಿಧ ಅಧ್ಯಯನೊಲೆನ್ ಬೊಕ್ಕ ವಿಮರ್ಶೆಲೆನ್ ಸಂಕಲನ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಮಲಯಾಳಂ ಭಾಷೆಡ್ ಅಡಮಿಂತೆ ಮಕನ್ ಅಬು: ದ್ೃಷ್ಯದರ್ಸನಂಗಳಿಲೆ ಕಾಲವುಂ ದೇಸವುಂ (ಅಬು, ಆಡಮ್ ನ ಮಗೆ: ದೃಶ್ಯ ಅನುಭವೊಡು ಕಾಲ ಬೊಕ್ಕ ಜಾಗ) ಪನ್ಪಿನ ಪುಸ್ತಕೊನು ಸಂಪಾದನೆ ಮಲ್ತೆರ್. ಈ ಬೂಕುನ್ ಕೇರಳೊದ ಪ್ರಮುಖ ಪ್ರಕಾಶನ ಸಂಸ್ಥೆ ಆಯಿನ ಮಾತೃಭೂಮಿ ಬುಕ್ಸ್ ಪ್ರಕಟ ಮಲ್ತೊಂದುಂಡು.<ref name="The Hindu review">{{cite news|url=http://www.hindu.com/2011/06/26/stories/2011062650370200.htm|archive-url=https://web.archive.org/web/20111102122401/http://www.hindu.com/2011/06/26/stories/2011062650370200.htm|url-status=dead|archive-date=2 November 2011|title=An honest portrayal of a man's battles within|author=S. Anandan|location=India|date=26 June 2011|work=[[The Hindu]]|access-date=2 July 2011}}</ref> ದಿ ಹಿಂದೂಸ್ತಾನ್ ಟೈಮ್ಸ್ ದ ಗೌತಮನ್ ಭಾಸ್ಕರನ್ ಈ ಚಿತ್ರೊಗು ಐನ್ ಡ್ದ್ ಮೂಜಿ ಸ್ಟಾರ್ ಕೊರ್ತೆರ್. ಅರೆನ ಪ್ರಕಾರ, ಅಬು ಬೊಕ್ಕ ಐಶುಮ್ಮನ ಮಗೆನ್ ಬುಡ್ದು ಚಿತ್ರೊಡು ಉಪ್ಪುನ ಬಹುತೇಕ ಪಾತ್ರೊಲೆನ್ ಅತಿಯಾದ್ ಆದರ್ಶೀಕೃತವಾದ್ ತೋಜಾವೆರ್. ಈ ಸಿನಿಮಾ "ಶಾಂತಿಯಾದ್ ಬರ್ಪಿನ ಒಂಜಿ ಸ್ಟ್ರೀಮ್" ದಲೆಕ ಪರಪುಂಡು ಪಂದ್ ಅರೆನ ಅಭಿಪ್ರಾಯ. ಅದೇ ಸಮಯೊಡು, ಚಿತ್ರೊಡು ಉಪ್ಪುನ ಸಂಯಮೊನು ಒಂಜಿ ಅಪರೂಪದ ಗುಣ ಪಂಡ್ದ್ ಮೆಚ್ಚಿಯೆರ್. ಛಾಯಾಗ್ರಹಣೊಡು ಕೆಲವು ದೃಶ್ಯೊಲು ಅಪೂರ್ವವಾದ್ ಸುಂದರವಾದ್ ಉಪ್ಪುನವು ಬೊಕ್ಕ ದುಃಖ, ನಿರಾಸೆ ಬೊಕ್ಕ ಒಂಟಿತನೊದಂಚಿನ ಭಾವನೆಲೆನ್ ಪರಿಣಾಮಕಾರಿಯಾದ್ ತೆರಿಪಾವುಂಡು ಪಂಡ್ದ್ ಆರ್ ಪಂಡೆರ್. ನಿರಾಸೆಡ್ದ್ ಸಂತೋಷ ಬೊಕ್ಕ ಆಶಾವಾದೊಗು ಪೋಪಿನ ಚಿತ್ರೊದ ಭಾವನಾತ್ಮಕ ವಾತಾವರಣೊನು ಸೂಕ್ತವಾದ್ ಸೃಷ್ಟಿ ಮಲ್ತಿನ ಐಸಾಕ್ ಥಾಮಸ್ ಕೊಟ್ಟಕಪಲ್ಲಿನ ಹಿನ್ನೆಲೆ ಸಂಗೀತೊನು ಆರ್ ಮೆಚ್ಚಿಯೆರ್. ನಟೆರೆನ ಅಭಿನಯೊನು, ವಿಶೇಷವಾದ್ ಪ್ರಮುಖ ಪಾತ್ರೊಲೆನ ಅಭಿನಯೊನು "ಅದ್ಭುತ" ಪಂಡ್ದ್ ಆರ್ ಲೆಪ್ಪುವೆರ್.<ref>{{cite news|url=http://movies.ndtv.com/movie_story.aspx?section=Movies&Id=ENTEN20110186323&keyword=regional&subcatg=MOVIESINDIA&nid=142393 |title=Adaminte Makan Abu can get us Oscar, says Director |author=[[Press Trust of India]] |publisher=[[NDTV]] |date=19 October 2011 |access-date=27 October 2011 |url-status=dead |archive-url=https://web.archive.org/web/20111020123253/http://movies.ndtv.com/movie_story.aspx?Section=Movies&ID=ENTEN20110186323&subcatg=MOVIESINDIA&keyword=regional&nid=142393 |archive-date=20 October 2011 }}</ref> ಈಸ್ಟರ್ನ್ಕಿಕ್ಸ್ ವಿಮರ್ಶಕೆರ್ ಚಿತ್ರೊಗು ಐನ್ ಡ್ದ್ ಐನ್ ನಕ್ಷತ್ರೊಲೆನ್ ಕೊರ್ತೆರ್, ಸರಳ ಕತೆನ್ ಮೆಚ್ಚಿಯೆರ್, ಅತ್ಯುತ್ತಮ ಛಾಯಾಗ್ರಹಣ ಬೊಕ್ಕ ಅತ್ಯುತ್ತಮ ಪ್ರದರ್ಶನೊಲೆನ್ ಸುಂದರವಾದ್ ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್. ಕೇರಳೊಡು ಮುಸ್ಲಿಂ ಜೀವನದ ಬಗ್ಗೆ ಒಂಜಿ ಅಪರೂಪದ ದೃಷ್ಟಿಕೋನೊನು ಈ ಸಿನಿಮಾ ಕೊರ್ಪುಂಡು ಪಂದ್ ಲಾ ಅಕ್ಲೆಗ್ ಅನಿಸಿಕೆ ಆಂಡ್. ಇನ್ಸ್ಟಿಟ್ಯೂಟ್ ಆಫ್ ಕಂಟೆಂಪರರಿ ಆರ್ಟ್ಸ್ ತನ್ನ ಅಧಿಕೃತ ವಿಮರ್ಶೆಡ್ ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬುನ್ "ಅಜೇಯ ಆಶಾ" ಬೊಕ್ಕ ನಿಜವಾದ್ ಸ್ನೇಹದ ಕತೆನ್ ಮಸ್ತ್ ಮೃದುವಾದ್ ತೆರಿಪಾವುನ ಒಂಜಿ ಚಿತ್ರ ಪಂಡ್ದ್ ವರ್ಣನೆ ಮಲ್ತ್ಂಡ್. ಗೈ ಲಾಡ್ಜ್ ಆಫ್ ವೆರೈಟಿ ಮ್ಯಾಗಜಿನ್ ಗ್ ಈ ಚಿತ್ರ ಸ್ಪಷ್ಟತೆ ಬೊಕ್ಕ ಸಹಾನುಭೂತಿಡ್ ಕತೆನ್ ಪನ್ಪುಂಡು ಪಂದ್ ಅನಿಸಿಕೆ ಆಂಡ್. ಆಂಡಲಾ, ಚಿತ್ರೊಡು ಹಾಸ್ಯ ಅತ್ತಂದೆ ಸಂಘರ್ಷದ ತೀವ್ರತೆದ ಕೊರತೆನ್ ಆರ್ ಟೀಕೆ ಮಲ್ತೆರ್, ಉಂದು ನಾಟಕೀಯ ಸಂಘರ್ಷೊನುಲಾ ಕಮ್ಮಿ ಮಲ್ಪುಂಡು. ಮಧು ಅಂಬತ್ ನ ಛಾಯಾಗ್ರಹಣೊನು ಆರ್ ಮೆಚ್ಚಿಯೆರ್, ಆಂಡ ಸಲೀಂ ಕುಮಾರ್ ನ ಅಭಿನಯೊನು ಮಸ್ತ್ ಉದ್ದೇಶಪೂರ್ವಕವಾದ್ ಮಲ್ತೆರ್ ಪಂದ್ ಆರ್ ಟೀಕೆ ಮಲ್ತೆರ್, ಆರ್ ಪಾತ್ರೊಗು ಬದ್ಧತೆ ಇತ್ತ್ಂಡಲಾ. ಕೆನಡಾದ ವೆಬ್ ಸೈಟ್ ಟ್ವಿಚ್ ಫಿಲ್ಮ್ ದ ಜೆ. ಹುರ್ಟಾಡೊ ಈ ಚಿತ್ರೊನು "ಶಾಂತ ಬೊಕ್ಕ ಧ್ಯಾನ" ಪಂಡ್ದ್ ವರ್ಣನೆ ಮಲ್ತೆರ್, ಉಂದು ಮನಸ್ಸ್ ಗ್ ಒಂಜಿ ರಿಫ್ರೆಶ್ ಅನುಭವೊನು ಒದಗಾವುಂಡು ಪಂಡ್ದ್ ಪನ್ಪೆರ್. ದಿ ಹಾಲಿವುಡ್ ರಿಪೋರ್ಟರ್ ದ ಲಿಸಾ ಟ್ಸೆರಿಂಗ್ ಚಿತ್ರದ ಪ್ರದರ್ಶನೊಲೆನ್ ಬೊಕ್ಕ ಪ್ರಕೃತಿ ದೃಶ್ಯೊಲೆನ್ ಮೆಚ್ಚಿಯೆರ್, ಆಂಡ ಉಂದು ಒಂಜಿ "ಆರ್ಟ್ ಹೌಸ್" ಚಿತ್ರ ಪಂಡ್ದ್ ಅನಿಸಿಕೆ ಮಲ್ತೆರ್, ಉಂದು ಮುಖ್ಯವಾದ್ ಒಂಜಿ ಗೂಡು ಪ್ರೇಕ್ಷಕೆರೆಗ್ ಗುರಿ ಆತ್ಂಡ್, ಬೊಕ್ಕ ಆ ಪ್ರೇಕ್ಷಕೆರೆನ ಪಿದಯಿ ಮಸ್ತ್ ಯಶಸ್ಸ್ ನ್ ಕಂಡುಕೊನೆರೆ ಕಷ್ಟ ಆಪುಂಡು. ರೆಡಿಫ್ ಡಾಟ್ ಕಾಮ್ ದ ಪರೇಶ್ ಸಿ. ಪಾಲಿಚ್ ಈ ಚಿತ್ರೊನು "ಸರಳ ಬೊಕ್ಕ ಪೊರ್ಲು" ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ನಿರ್ದೇಶನ, ಕತೆ ಬೊಕ್ಕ ಅಭಿನಯೊಲೆನ್ ಆರ್ ವಿಶೇಷವಾದ್ ಮೆಚ್ಚಿಯೆರ್. ಆಂಡಲಾ, ಉದ್ದೇಶಪೂರ್ವಕವಾದ್ ಅತಿ-ಭಾವನಾತ್ಮಕ ಪ್ರಭಾವ ಬೊಕ್ಕ ಒಂಜಿ ಟ್ರಾವೆಲ್ ಏಜೆನ್ಸಿದ ಅತಿ ಸಕಾರಾತ್ಮಕ ಚಿತ್ರಣ ಇಂಚಿನ ಕೆಲವು ದುರ್ಬಲತೆಲೆನ್ ಆರ್ ಗುರುತಿಸಯೆರ್. ಆರ್ ಈ ಸಿನಿಮಾಗ್ ಐನ್ ಡ್ದ್ ಮೂಜಿ ಸ್ಟಾರ್ ಲೆನ್ ಕೊರ್ತೆರ್. ಡೆಕ್ಕನ್ ಕ್ರಾನಿಕಲ್ ದ ಕೀರ್ತಿ ರಾಮಚಂದ್ರನ್ ಈ ಚಿತ್ರೊಗು ಐನ್ ಡ್ದ್ ಐನ್ ಸ್ಟಾರ್ ಲೆನ್ ಕೊರ್ದು, ಈ ಚಿತ್ರೊಗು ಮಲಯಾಳಂ ಸಿನಿಮಾದ ಇತಿಹಾಸೊಡು ಗೌರವೊದ ಸ್ಥಾನ ಉಂಡು ಪಂದ್ ಬರೆತೆರ್. ದಿ ಹಿಂದೂ ಚಿತ್ರದ ಎಸ್.ಆನಂದನ್ ಈ ಸಿನಿಮಾಗ್ ಸಕಾರಾತ್ಮಕ ವಿಮರ್ಶೆ ಕೊರ್ತೆರ್. ಆಧ್ಯಾತ್ಮಿಕ ಶಾಂತಿದ ಅನ್ವೇಷಣೆದ ಒರಿ ಧರ್ಮನಿಷ್ಠ ಮಲ್ಲ ಮನುಷ್ಯನ ಅನ್ವೇಷಣೆನ್ ಚಿತ್ರೊಡು ತೋಜಾವುನವು ಮಲಯಾಳಂ ಸಿನಿಮಾಗ್ ಒಂಜಿ ಆಶಾದಾಯಕ ಅಭಿವೃದ್ಧಿ ಪಂಡ್ದ್ ಅರೆಗ್ ಅನಿಸಿಕೆ ಆಂಡ್. ಆಂಡಲಾ, ಚಿತ್ರದ ಗ್ರಾಮೀಣ ಸೆಟ್ಟಿಂಗ್ ನ್ ಅತಿ ಆದರ್ಶವಾದಿ ಪಂಡ್ದ್ ಆರ್ ಟೀಕೆ ಮಲ್ತೆರ್. ಬೇತೆ ಬೇತೆ ಧರ್ಮೊಗು ಸೇರ್ದಿನ ಜನಕುಲು ಒಂಜೊಂಜಿ ಸೌಹಾರ್ದೊಡು ಜೀವನ ಮಲ್ಪುನ "ಬಶಿರಿಯನ್" ರೀತಿದ ಲೋಕೊನು ನಿರ್ದೇಶಕೆರ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್ ಪಂಡ್ದ್ ಆರ್ ಪಂಡೆರ್. ಸಿಟಿ ಜರ್ನಲ್ ದ ಟಿ. ಸುಧೀಶ್ ಈ ಚಿತ್ರೊನು ಸುಂದರ, ಆಕರ್ಷಕ ಬೊಕ್ಕ ಮನರಂಜನೆದ ಚಿತ್ರ ಪಂಡ್ದ್ ವರ್ಣನೆ ಮಲ್ತೆರ್, ಉಂದು ನಿಜವಾದ್ ಲಾ ರಾಷ್ಟ್ರೀಯ ಪ್ರಶಸ್ತಿಗ್ ಅರ್ಹವಾಯಿನವು ಪಂಡ್ದ್ ಪನ್ಪೆರ್. Nowrunning.com ದ ವೀನ್ ಚಿತ್ರದ ನಿಖರವಾದ್ ಗತಿ ಬೊಕ್ಕ ಅದ್ಭುತ ದೃಶ್ಯ ಸಂಯೋಜನೆನ್ ಮೆಚ್ಚಿಯೆರ್. ಕಷ್ಟದ ಸಮಯೊಡುಲಾ ಉಂದು ಮಾನವಕುಲೊಗು ಒಂಜಿ ಆಶಾಭಾವನೆನ್ ಕೊರ್ಪುಂಡು ಪಂದ್ ಅರೆಗ್ ಅನಿಸಿಕೆ ಆಂಡ್. ಆರ್ ಈ ಸಿನಿಮಾಗ್ ಐನ್ ಡ್ದ್ ಮೂಜಿ ಸ್ಟಾರ್ ಲೆನ್ ಕೊರ್ತೆರ್. ಸಿಫೈ ಡಾಟ್ ಕಾಮ್ ದ ವಿಮರ್ಶಕೆರ್ ಈ ಸಿನಿಮಾಗ್ ಐನ್ ಸ್ಟಾರ್ ಡ್ ಐನ್ ಸ್ಟಾರ್ ಕೊರ್ದು, ಉಂದು ಮಲಯಾಳಂ ಸಿನಿಮಾಗ್ ಹೆಮ್ಮೆನ್ ತೂಪಿನ ಸಿನಿಮಾ ಬೊಕ್ಕ ಮಲಯಾಳಂ ಭಾಷೆಡ್ ಮಲ್ತಿನ ಅತ್ಯಂತ ಶಕ್ತಿಶಾಲಿ ಸಿನಿಮಾಲೆಡ್ ಒಂಜಿ ಸ್ಥಾನ ಪಡೆಯೊಡು ಪಂಡ್ ದ್ ಪನ್ಪೆರ್. Oneindia.in ದ ವಿಮರ್ಶಕೆರ್ ಸರಳ ಕತೆನ್ ಮೆಚ್ಚಿಯೆರ್, ಉಂದೆನ್ ನಿರ್ದೇಶಕೆರ್ ಸಲೀಮ್ ಅಹ್ಮದ್ ಒಂಜಿ ಅದ್ಭುತ ಚಿತ್ರಕಥೆ ಆದ್ ಮಲ್ತೆರ್. ದಿ ಡೆಸರ್ಟ್ ಸನ್ ದ ಬ್ರೂಸ್ ಫೆಸ್ಸಿಯರ್ ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬುನ್ ಧಾರ್ಮಿಕ ಭಕ್ತಿದ ಅರ್ಥೊನು ವಿವರಿಸವುನ "ಸಿಹಿ ಬೊಕ್ಕ ಮೃದು ಕತೆ" ಪಂಡ್ದ್ ವರ್ಣನೆ ಮಲ್ತೆರ್. ಅಬು ಬೊಕ್ಕ ಐಶುಮ್ಮ ಅಕ್ಲೆನ ಜೀವನೊಡು ಬರ್ಪಿನ ಪ್ರತಿಯೊಂಜಿ ಫಲಿತಾಂಶೊನು ದೇವೆರೆನ ಇಚ್ಛೆ ಪಂಡ್ದ್ ಸ್ವೀಕಾರ ಮಲ್ಪುವೆರ್; ಅಕ್ಲೆನ ಮಗೆ ಮಲ್ತಿನ ನಿರಾಸೆನ್ ಬುಡ್ದು ಅಕುಲು ಒವ್ವೆ ಪಶ್ಚಾತ್ತಾಪೊನು ತೋಜಾವುಜೆರ್ ಪಂದ್ ಅರೆನ್ ಗಮನಿಸಯೆರ್. ಧಾರ್ಮಿಕ ನಂಬಿಕೆ ಬೊಕ್ಕ ಭವಿಷ್ಯದ ನಂಬಿಕೆ ಅಕ್ಲೆಗ್ ಇತ್ತೆದ ಜೀವನದ ನಿರಾಸೆಲೆನ್ ಜಯಿಪುನ ಶಕ್ತಿನ್ ಕೊರ್ಪುಂಡು ಪನ್ಪಿನವು ಚಿತ್ರದ ಮುಖ್ಯ ಸಂದೇಶ ಪಂದ್ ಆರ್ ಅಭಿಪ್ರಾಯ ಮಲ್ತೆರ್. ಆರ್ ಈ ಚಿತ್ರೊಗು ಐನ್ ಡ್ದ್ ಏಳ್ ನಕ್ಷತ್ರೊಲೆನ್ ಕೊರ್ತೆರ್ ಬೊಕ್ಕ ಪ್ರಮುಖ ನಟೆರೆನ ಅಭಿನಯೊನು ವಿಶೇಷವಾದ್ ಮೆಚ್ಚಿಯೆರ್. ಪುದರ್ ಪೋಯಿನ ವಾಗ್ಮಿ ಬೊಕ್ಕ ಲೇಖಕಿ ಸುಕುಮಾರ್ ಅಜಿಕೋಡೆ ಈ ಸಿನಿಮಾ ತೂದು ಬೊಕ್ಕ ಪನ್ಪೆರ್, "ಒಂಜಿ ಮಲಯಾಳಂ ಸಿನಿಮಾ ಎಂಚ ಉಪ್ಪೊಡು ಪನ್ಪಿನೆನ್ ಉಂದು ನಮಕ್ ತೋಜಾವುಂಡು". ಸಲೀಂ ಕುಮಾರ್ ನ ಅಭಿನಯೊನು ವಿಶೇಷವಾದ್ ಮೆಚ್ಚಿಯೆರ್. ಬ್ರೂಸ್ ಫೆಸ್ಸಿಯರ್ ನಲೆಕನೆ, ಅಜಿಕೋಡೆ ಪನ್ಪೆರ್ ಸಲೀಮ್ ಕುಮಾರ್ ನಟನೆಡ್ದ್ ಜಾಸ್ತಿ ಅಬುನ್ ಜೀವನ ಮಲ್ಪುನಂಚಿನ ಅನುಭವ ಆಪುಂಡು. == ನಕಾರಾತ್ಮಕ ವಿಮರ್ಶೆಲು == ಚಿತ್ರೊಗು ಕೆಲವು ನಕಾರಾತ್ಮಕ ವಿಮರ್ಶೆಲುಲಾ ಬತ್ತ್ಂಡ್. ಉದಾಹರಣೆಗೆ, ಸಿನಿ-ವ್ಯೂ.ಕಾಮ್ ದ ಡೇನಿಯಲ್ ಗ್ರೀನ್ ಚಿತ್ರೊಗು ಐನ್ ಡ್ದ್ ರಡ್ಡ್ ನಕ್ಷತ್ರೊಲೆನ್ ಕೊರ್ತೆರ್, ಈ ಚಿತ್ರ ಕೇರಳದ ಮುಸ್ಲಿಂ ಸಮುದಾಯೊದ ಬಗ್ಗೆ ಮಸ್ತ್ ಒಳನೋಟೊನು ಕೊರ್ಪುಜಿ ಪನ್ಪಿನ ಭಾವನೆಡ್. ಅರೆನ ಪ್ರಕಾರ, ಚಿತ್ರ "ಮಲ್ಲ ಅತ್ತ್, ಪರಿಣಾಮಕಾರಿ ಅತ್ತ್, ಅತ್ತಂದೆ ನವೀನವಾಯಿನವು ಅತ್ತ್". ಒಟ್ಟಾರೆ, ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬು ತನ್ನ ಸರಳ ನಿರೂಪಣೆ, ಮಾನವ ಮೌಲ್ಯೊಲು, ಧಾರ್ಮಿಕ ಸೌಹಾರ್ದತೆ, ಪರಿಸರ ಪ್ರಜ್ಞೆ, ಅತ್ಯುತ್ತಮ ಪ್ರದರ್ಶನೊಲು, ಛಾಯಾಗ್ರಹಣ ಬೊಕ್ಕ ಸಂಗೀತದ ಮಧ್ಯಮ ಬಳಕೆಗ್ ವ್ಯಾಪಕವಾದ್ ಪ್ರಶಂಸೆ ಪಡೆಯೆರ್. ವಿಶೇಷವಾದ್ ಅಬು ಪಾತ್ರೊಡು ಸಲೀಮ್ ಕುಮಾರ್ ನಟನೆ ಮಲ್ತಿನವು ಚಿತ್ರೊದ ವಿಮರ್ಶಕೆರೆನ ಮೆಚ್ಚುಗೆಗ್ ಒಂಜಿ ಪ್ರಮುಖ ಕಾರಣ. == ಬಾಕ್ಸ್ ಆಫೀಸ್ == ಬಹುತೇಕ ಎಲ್ಲೆಡೆಡ್ದ್ ಸಕಾರಾತ್ಮಕ ವಿಮರ್ಶೆಲೆನ್ ಪಡೆಯಿನ ನಡುಟು, ಅಡಮಿಂತೆ ಮಕನ್ ಅಬು ಕೇರಳೊಡು ಬಾಕ್ಸ್ ಆಫೀಸ್ ಡ್ ಮಲ್ತಿನ ಪ್ರದರ್ಶನ ಸರಾಸರಿ ಮಾತ್ರ ಆದಿತ್ತ್ಂಡ್. ಚಿತ್ರ ಬಿಡುಗಡೆ ಆಯಿ ಬೊಕ್ಕ ಕೆಲವು ದಿನೊಕುಲೆಡ್ ಥಿಯೇಟರ್ ಡ್ದ್ ಪಿರ ದೆತ್ತ್ ದ್ ದೀತಿನೆಡ್ದಾವರ, ಮಸ್ತ್ ಪ್ರೇಕ್ಷಕೆರ್ ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬುನ್ ಥಿಯೇಟರ್ ಡ್ ತೂಯೆರೆ ಸಾಧ್ಯ ಆಯಿಜಿ. ಉಂದು ಸಿನಿಮಾದ ವಾಣಿಜ್ಯ ಪ್ರದರ್ಶನೊನ್ಲಾ ಪರಿಣಾಮ ಬೀರುಂಡು. ಚಿತ್ರದ ಥಿಯೇಟರ್ ಡ್ದ್ ದುಂಬು ಪಿರ ಬುಡುದು ಪೋಯಿನವುಲಾ ನನೊಂಜಿ ಸರ್ತಿ ಬಿಡುಗಡೆ ಆಯಿನ ಡಿವಿಡಿಗ್ ನಿರೀಕ್ಷೆಡ್ದ್ ಜಾಸ್ತಿ ಬೇಡಿಕೆಗ್ ಒಂಜಿ ಕಾರಣ ಪಂಡ್ದ್ ವರದಿ ಆತ್ಂಡ್. ಸಲೀಂ ಕುಮಾರ್ ಪನ್ಪಿ ಪ್ರಕಾರ ಕೇರಳದ ಬಹುತೇಕ ಭಾಗೊಡು ಈ ಸಿನಿಮಾದ ಬಾಕ್ಸ್ ಆಫೀಸ್ ಪ್ರದರ್ಶನ ಎಡ್ಡೆ ಇಜ್ಜಿ. ಆಂಡಲಾ ಮುಸ್ಲಿಂ ಬಹುಸಂಖ್ಯಾತ ಮಲಬಾರ್ ಪ್ರದೇಶೊಡು ಈ ಸಿನಿಮಾಗ್ ಎಡ್ಡೆ ಸ್ಪಂದನೆ ಬತ್ತ್ಂಡ್. ಅಂಚ, ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬು ವಿಮರ್ಶಾತ್ಮಕವಾದ್ ಬೊಕ್ಕ ಪ್ರಶಸ್ತಿಲೆನ ಲೆಕ್ಕೊಡು ಮಲ್ಲ ಯಶಸ್ಸು ಪಡೆಯಿನ ನಡುಟು, ರಂಗಮಂದಿರೊಲೆಡ್ ಅಯಿತ ವಾಣಿಜ್ಯ ಯಶಸ್ಸು ಸೀಮಿತವಾದಿತ್ತ್ಂಡ್. ಚಿತ್ರದ ವಿಮರ್ಶಕೆರೆನ ಮೆಚ್ಚುಗೆ ಬೊಕ್ಕ ರಾಷ್ಟ್ರೀಯ ಪ್ರಶಸ್ತಿಲೆನ್ ಪಡೆಯಿನ ನಡುಟು, ಅಯಿತ ಸಣ್ಣ ನಾಟಕೀಯ ರನ್ ಅಯಿತ ಬಾಕ್ಸ್ ಆಫೀಸ್ ಗಳಿಕೆನ್ ಸೀಮಿತ ಮಲ್ತ್ಂಡ್. == 2012 ಅಕಾಡೆಮಿ ಪ್ರಶಸ್ತಿ ಪ್ರದಾನೊಡು ಅತ್ಯುತ್ತಮ ವಿದೇಶಿ ಭಾಷೆದ ಚಿತ್ರ ಪ್ರವೇಶ == ಅಡಮಿಂತೆ ಮಕನ್ ಅಬು ಅತ್ಯುತ್ತಮ ವಿದೇಶಿ ಭಾಷೆದ ಚಿತ್ರ ವಿಭಾಗೊಡು 84ನೇ ಅಕಾಡೆಮಿ ಪ್ರಶಸ್ತಿ (2012) ಗ್ ಭಾರತದ ಅಧಿಕೃತ ಪ್ರವೇಶ. ದೇಶೊನು ಪ್ರತಿನಿಧಿಸರೆ ಸಲ್ಲಿಸಾಯಿನ 16 ಚಿತ್ರೊಲೆಡ್ ಫಿಲ್ಮ್ ಫೆಡರೇಶನ್ ಆಫ್ ಇಂಡಿಯಾ (ಎಫ್ ಎಫ್ ಐ) ಅಡಮಿಂಟೆ ಮಕನ್ ಅಬುನ್ ಆಯ್ಕೆ ಮಲ್ತ್ಂಡ್. ಆಸ್ಕರ್ ಪ್ರಶಸ್ತಿಗ್ ಸಲ್ಲಿಸಾಯಿನ ರಡ್ಡನೆ ಮಲಯಾಳಂ ಸಿನಿಮಾ ಆದಿತ್ತ್ಂಡ್. ದುಂಬುದ ಪ್ರವೇಶ ಗುರು (೧೯೯೭). == ರಿಮೇಕ್ ಯೋಜನೆಲು == ಮೇ ೨೦೧೧ ದ ಕಡೆಟ್, ಮಲಯಾಳಂ ದೈನಿಕ ಮಧ್ಯಮಂ ಮುಂಬೈ ಮೂಲದ ನಿರ್ಮಾಣ ಕಂಪೆನಿ ಮೆಗಾ ಪ್ರೊಡಕ್ಷನ್ಸ್ ನಿರ್ದೇಶಕ ಸಲೀಮ್ ಅಹ್ಮದ್ ನ್ ಅಡಮಿಂತೆ ಮಕನ್ ಅಬುನ್ ಹಿಂದಿಡ್ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ಸಂಪರ್ಕ ಮಲ್ತ್ಂಡ್ ಪಂದ್ ವರದಿ ಮಲ್ತ್ಂಡ್. ಮೂಲ ಚಿತ್ರದ ರಂಗಮಂದಿರೊಡು ಬಿಡುಗಡೆ ಆಪಿನ ದುಂಬು ಜೂನ್ ಸುರುಟು ಚರ್ಚೆಲು ನಡಪೊಡು. ನನೊಂಜಿ ಸರ್ತಿ, ಕರಣ್ ಜೋಹರ್ ನ ಧರ್ಮ ಪ್ರೊಡಕ್ಷನ್ಸ್ ಚಿತ್ರದ ರೀಮೇಕ್ ರೈಟ್ಸ್ ನ್ ಕೊಂಡೊನೆರೆ ಸಲೀಮ್ ಅಹ್ಮದ್ ನ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್ ಪನ್ಪಿನ ವರದಿ ಬತ್ತ್ಂಡ್. ನಟಿ ಜರೀನಾ ವಹಾಬ್ ಡ್ದ್ ಕರಣ್ ಜೋಹರ್ ಈ ಸಿನಿಮಾದ ಬಗ್ಗೆ ಕೇಂಡಿನ ವರದಿ ಬತ್ತ್ಂಡ್. ಆ ವರದಿಲೆ ಪ್ರಕಾರ, ಕರಣ್ ಜೋಹರ್ ಹಿಂದಿ ರೀಮೇಕ್ ನ್ ನಿರ್ದೇಶನ ಮಲ್ಪುವೆರ್, ಶಾರುಖ್ ಖಾನ್ ಅಬು ಪಾತ್ರೊನ್ ಮಲ್ಪುವೆರ್ ಬೊಕ್ಕ ಸಲೀಮ್ ಅಹ್ಮದ್ ಲಾ ಈ ಯೋಜನೆಗ್ ಸಂಬಂಧ ಪಟ್ಟಿನಕುಲು. ಆಂಡಲಾ, ಕರಣ್ ಜೋಹರ್ ನನೊಂಜಿ ಸರ್ತಿ ಈ ವರದಿಲೆನ್ ಸ್ಪಷ್ಟವಾದ್ ನಿರಾಕರಣೆ ಮಲ್ತೆರ್. ಒಂಜಿ ಟ್ವಿಟ್ಟರ್ ಪೋಸ್ಟ್ ಡ್, ಮೆಚ್ಚುಗೆ ಪಡೆಯಿನ ಮಲಯಾಳಂ ಚಿತ್ರೊನು ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪುನ ಯೋಜನೆ ಇಜ್ಜಿ ಪಂದ್ ಆರ್ ಸ್ಪಷ್ಟಪಡಿಸಯೆರ್. ಸಲೀಮ್ ಅಹ್ಮದ್ ನನೊಂಜಿ ಸರ್ತಿ ಧರ್ಮ ಪ್ರೊಡಕ್ಷನ್ಸ್ ಚಿತ್ರದ ಹಕ್ಕುಲೆನ್ ಕೊಂಡೊನೆರೆ ತನ್ನನ್ ಸಂಪರ್ಕ ಮಲ್ತೆರ್ ಪಂದ್ ತೆರಿಪಾಯೆರ್. ಆಂಡಲಾ, ಸುರುತ ಪಾತೆರೊದ ನಂತರ, ಕಂಪೆನಿಡ್ದ್ ದಾಲ ಪ್ರತಿಕ್ರಿಯೆ ಇಜ್ಜಿ ಪಂಡ್ದ್ ಆರ್ ಪಂಡೆರ್. ಸಲೀಮ್ ಅಹ್ಮದ್ ನಿರ್ದೇಶಕ ಜೆ. ಪಿ. ದತ್ತನೊಟ್ಟುಗು ಚಿತ್ರದ ಹಿಂದಿ ರೀಮೇಕ್ ದ ಬಗ್ಗೆಲಾ ಚರ್ಚೆ ಮಲ್ತೆರ್. ಹಿಂದಿಡ್ ಯಶಸ್ವಿ ಆಯಿನ ಚಿತ್ರೊನು ಮರುನಿರ್ಮಾಣ ಮಲ್ಪೆರೆ ದತ್ತಾಗ್ ಆಸಕ್ತಿ ಇತ್ತ್ಂಡ್. ಆಂಡಲಾ, ಆ ಯೋಜನೆಲಾ ಸಾಕಾರ ಆಯಿಜಿ. ನವೆಂಬರ್ ೨೦೧೧ಡ್ ಒಂಜಿ ಸಂದರ್ಶನೊಡು ಸಲೀಂ ಕುಮಾರ್ ಈ ಸಿನಿಮಾನ್ ನಾಲ್ ಪ್ರಮುಖ ಭಾರತೀಯ ಬಾಸೆಲೆಡ್ ಮರುನಿರ್ಮಾಣ ಮಲ್ಪುನ ಯೋಜನೆ ಉಂಡು ಪಂಡ್ದ್ ಪನ್ಪೆರ್. ಆಂಡ ಸಲೀಮ್ ಅಹ್ಮದ್ ಕಡೆಕ್ ಚಿತ್ರದ ರೀಮೇಕ್ ರೈಟ್ಸ್ ನ್ ಏರೆಗ್ಲಾ ಕೊರ್ರೆ ಬಯಕುಜಿ ಪಂಡ್ದ್ ಸ್ಪಷ್ಟ ಮಲ್ತೆರ್. ಅಂಚಾದ್, ಹಿಂದಿ ಸೇರ್ದ್ ಬೇತೆ ಬಾಸೆಲೆಡ್ ಅಡಮಿಂತೆ ಮಕನ್ ಅಬುನ್ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ಪುನ ಬಗ್ಗೆ ಮಸ್ತ್ ಪ್ರಸ್ತಾವೊಲು ಬೊಕ್ಕ ಚರ್ಚೆಲು ಇತ್ತ್ಂಡಲಾ, ಅವುಲು ಒವ್ವೆಲಾ ಅಂತಿಮವಾದ್ ಸಾಕಾರ ಆಯಿಜಿ. ==ಉಲ್ಲೇಕೊಲು== 92667mzh0sio46g2lwlu4ivpzt7itww ಬಳಕೆದಾರೆ:ChiK/MoveToDraft 2 27959 366205 365735 2026-08-21T10:43:03Z ChiK 1136 366205 wikitext text/x-wiki {{Infobox user script | name = MoveToDraft | desc = Moves articles to draft space, usually used for undersourced articles | author = [[User:Evad37|Evad37]] & [[User:MPGuy2824|MPGuy2824]] | maintainer = [[User:ChiK|ChiK]] | version = 2.5.8 | updated = {{Date|2023-07-26}} ([https://gitlab.wikimedia.org/mpg/moveToDraft history] and {{History|User:Evad37/MoveToDraft.js|older history}}) | source = [[User:ChiK/MoveToDraft.js]] | skins = {{plainlist| * '''{{y&}}''' Vector (2022) * '''{{y&}}''' Vector legacy (2010) * '''{{y&}}''' Monobook * '''{{y&}}''' Modern * '''{{y&}}''' Cologne Blue * '''{{y&}}''' Timeless * '''{{n&}}''' MinervaNeue }} | screenshot = MoveToDraft - Options screen.png | screenshot-caption = Screenshot of version 2.5.8 of the script in action | styled = yes }} {{Shortcut|WP:MTD|WP:MOVETODRAFT}} '''MoveToDraft''' ([[User:ChiK/MoveToDraft.js|source]]) – A script that is used to [[:en:WP:DRAFTIFY|draftify]] articles (move to draft space), including cleanup and contributor notification. Steps performed by the script: # Move the current page to [[:en:Wikipedia:Drafts|draftspace]] and, if the user isn't an admin or page mover, then tag the redirect left behind for [[:en:WP:R2|R2 speedy deletion]]. # Edit page to comment out any non-free files, put categories in {{tl|Draft categories}}, and add the {{tl|Draft article}}<!--{{tl|AFC draft}}--> template. # Post a notification message to significant contributors' talk page/s. # Update talk page banners. # Log the move to the [[Special:MyPage/Draftify log|/Draftify log]] subpage of the user's space. ==Installation== ===Automatic=== * '''Step 1''': Check the checkbox at {{myprefs|Gadgets|Advanced|check={{int:Gadget-script-installer}}}} and click Save. * '''Step 2''': Click the big blue "Install" button in the infobox on the right of this page. ===Manual=== * Add the following to [[Special:MyPage/common.js]] (or your skin-specific js, if you prefer): {{tls|iusc|User:ChiK/MoveToDraft.js}} ==Usage== # On articles, there will be an additional "Move to draft" menu option next to the regular "Move" option. In Vector skin, this is within the "More" dropdown menu. # [[File:Move To Draft - ChooseContributors screen.png|thumb|Choosing the contributors to send the notification to]] Selecting "Move to draft" will bring up a list of contributors to whom notifications can be sent. This step is skipped if there is only one contributor to the page. # Next, a dialog box is shown where the new draft title, the username of the author to notify, and a set of checkboxes to indicate the problem/s with the current version of the article can be chosen. # [[File:Move To Draft - InProgress screen.png|thumb|The In-progress screen in the middle of its tasks]] When the "Continue" button is clicked, the script will undertake the steps listed above. If there are any errors encountered with a step, you generally have the option to retry or skip it. * '''Note''': for those without the [[:en:WP:PAGEMOVER|page mover]] user-right, the redirect left behind from moving the article to draftspace will be tagged for [[:en:WP:R2|CSD R2]] deletion automatically. ==Warnings== The script warns the user in certain conditions: * Previous draftification of the article * Old article (created more than 90 days ago) * New article (significant edit/s less than 1 hour ago) ==Draftify log== {{collapse top|This isn't working well and has been sunsetted.}} This script adds a pseudo-special page for viewing logs of draftifications – all draftifications, not just from this tool. Once installed, you can navigate to <span class="plainlinks>[https://en.wikipedia.org/wiki/Special:Draftify_log Special:Draftify log]</span> and view logs from all users, or a particular user. For convenience, a "Draftify log" link is placed on user pages (near the "Move to draft" menu option), linking to that user's log. A wikitext version of the log is generated in a textbox at the bottom of the page, for easy copy-pasting if desired. {{collapse bottom}} ==Customisation== * '''Watching pages''': If you want to watchlist the pages that you move, then check the checkbox at {{myprefs|Watchlist|Watched pages|check={{int:Add pages and files I move to my watchlist}}}} and click Save. * '''Subscribing to user talk page discussions''': If you want to auto-subscribe to user talk page discussion, then check the checkbox at {{myprefs|Editing|Discussion pages|check={{int:Enable topic subscription }}}} and click Save. * '''"Move to draft" link placement''': Add the line <code>var m2d_portlet = "{{gi|portletID}}";</code> to [[Special:MyPage/common.js|your common.js]], replacing {{kbd|{{gi|portletID}}}} with one of the portlet id values from [[Help:Customizing toolbars]] (the script's default location is <code>p-cactions</code>) * '''Move log rationale''': Add the line <code>var m2d_rationale = "{{gi|''rationale''}}";</code> to [[Special:MyPage/common.js|your common.js]], replacing {{kbd|{{gi|rationale}}}} with custom text. The default rationale is {{samp|<nowiki>[[WP:DRAFTIFY|Not ready]]</nowiki> for mainspace, incubate in draftspace}} * '''Edit summary for preparing article for draftspace''': Add the line <code>var m2d_editsummary = "{{gi|editSummary}}";</code> to [[Special:MyPage/common.js|your common.js]], replacing {{kbd|{{gi|editSummary}}}} with custom text. The default rationale is {{samp|<nowiki>[[WP:AFC|AFC]] draft</nowiki>}} * '''Disable logging within your userspace''': Add the line <code>var m2d_doNotLog = {{gi|true}};</code> to [[Special:MyPage/common.js|your common.js]] to disable logging moves (in [[Special:MyPage/Draftify log|/Draftify log]] within your userspace). Logging is enabled if this is omitted or set to {{samp|false}}. * '''Contributor notification message''' *: {{red|'''Use with caution''', and only if you are sure what you're doing:}} *: Add the line <code>var m2d_notification = "{{gi|Your message here}}";</code> to [[Special:MyPage/common.js|your common.js]] *: '''Important:''' within your message: *: * You need to use <code>$1</code> instead of the page title *: * You need to prevent wikitext transformations on the common.js page itself (i.e. <code>\~~\~~</code> instead of <code><nowiki>~~~~</nowiki></code>, <code><nowiki>\{\{subst:</nowiki></code> instead of <code><nowiki>{{subst:</nowiki></code> for any template substitution) *: * You need to escape any quotation marks (<code>"</code> characters) as <code>\"</code> to avoid syntax errors. *: * Any actual backslashes (<code>\</code> characters) need to be escaped as <code>\\</code>. * '''Location of your log file''': You can change the location of the <code>Draftify_log</code> file by creating a redirect from <code>User:Username/Draftify_log</code> to whatever location you prefer (as long as it is within your userspace). f979c3gg2fmivj16slxr3m8p4k89nda 366212 366205 2026-08-21T10:46:16Z ChiK 1136 /* Draftify log */ 366212 wikitext text/x-wiki {{Infobox user script | name = MoveToDraft | desc = Moves articles to draft space, usually used for undersourced articles | author = [[User:Evad37|Evad37]] & [[User:MPGuy2824|MPGuy2824]] | maintainer = [[User:ChiK|ChiK]] | version = 2.5.8 | updated = {{Date|2023-07-26}} ([https://gitlab.wikimedia.org/mpg/moveToDraft history] and {{History|User:Evad37/MoveToDraft.js|older history}}) | source = [[User:ChiK/MoveToDraft.js]] | skins = {{plainlist| * '''{{y&}}''' Vector (2022) * '''{{y&}}''' Vector legacy (2010) * '''{{y&}}''' Monobook * '''{{y&}}''' Modern * '''{{y&}}''' Cologne Blue * '''{{y&}}''' Timeless * '''{{n&}}''' MinervaNeue }} | screenshot = MoveToDraft - Options screen.png | screenshot-caption = Screenshot of version 2.5.8 of the script in action | styled = yes }} {{Shortcut|WP:MTD|WP:MOVETODRAFT}} '''MoveToDraft''' ([[User:ChiK/MoveToDraft.js|source]]) – A script that is used to [[:en:WP:DRAFTIFY|draftify]] articles (move to draft space), including cleanup and contributor notification. Steps performed by the script: # Move the current page to [[:en:Wikipedia:Drafts|draftspace]] and, if the user isn't an admin or page mover, then tag the redirect left behind for [[:en:WP:R2|R2 speedy deletion]]. # Edit page to comment out any non-free files, put categories in {{tl|Draft categories}}, and add the {{tl|Draft article}}<!--{{tl|AFC draft}}--> template. # Post a notification message to significant contributors' talk page/s. # Update talk page banners. # Log the move to the [[Special:MyPage/Draftify log|/Draftify log]] subpage of the user's space. ==Installation== ===Automatic=== * '''Step 1''': Check the checkbox at {{myprefs|Gadgets|Advanced|check={{int:Gadget-script-installer}}}} and click Save. * '''Step 2''': Click the big blue "Install" button in the infobox on the right of this page. ===Manual=== * Add the following to [[Special:MyPage/common.js]] (or your skin-specific js, if you prefer): {{tls|iusc|User:ChiK/MoveToDraft.js}} ==Usage== # On articles, there will be an additional "Move to draft" menu option next to the regular "Move" option. In Vector skin, this is within the "More" dropdown menu. # [[File:Move To Draft - ChooseContributors screen.png|thumb|Choosing the contributors to send the notification to]] Selecting "Move to draft" will bring up a list of contributors to whom notifications can be sent. This step is skipped if there is only one contributor to the page. # Next, a dialog box is shown where the new draft title, the username of the author to notify, and a set of checkboxes to indicate the problem/s with the current version of the article can be chosen. # [[File:Move To Draft - InProgress screen.png|thumb|The In-progress screen in the middle of its tasks]] When the "Continue" button is clicked, the script will undertake the steps listed above. If there are any errors encountered with a step, you generally have the option to retry or skip it. * '''Note''': for those without the [[:en:WP:PAGEMOVER|page mover]] user-right, the redirect left behind from moving the article to draftspace will be tagged for [[:en:WP:R2|CSD R2]] deletion automatically. ==Warnings== The script warns the user in certain conditions: * Previous draftification of the article * Old article (created more than 90 days ago) * New article (significant edit/s less than 1 hour ago) ==Draftify log== {{collapse top|This isn't working well and has been sunsetted.}} This script adds a pseudo-special page for viewing logs of draftifications – all draftifications, not just from this tool. Once installed, you can navigate to <span class="plainlinks>[https://tcy.wikipedia.org/wiki/Special:Draftify_log Special:Draftify log]</span> and view logs from all users, or a particular user. For convenience, a "Draftify log" link is placed on user pages (near the "Move to draft" menu option), linking to that user's log. A wikitext version of the log is generated in a textbox at the bottom of the page, for easy copy-pasting if desired. {{collapse bottom}} ==Customisation== * '''Watching pages''': If you want to watchlist the pages that you move, then check the checkbox at {{myprefs|Watchlist|Watched pages|check={{int:Add pages and files I move to my watchlist}}}} and click Save. * '''Subscribing to user talk page discussions''': If you want to auto-subscribe to user talk page discussion, then check the checkbox at {{myprefs|Editing|Discussion pages|check={{int:Enable topic subscription }}}} and click Save. * '''"Move to draft" link placement''': Add the line <code>var m2d_portlet = "{{gi|portletID}}";</code> to [[Special:MyPage/common.js|your common.js]], replacing {{kbd|{{gi|portletID}}}} with one of the portlet id values from [[Help:Customizing toolbars]] (the script's default location is <code>p-cactions</code>) * '''Move log rationale''': Add the line <code>var m2d_rationale = "{{gi|''rationale''}}";</code> to [[Special:MyPage/common.js|your common.js]], replacing {{kbd|{{gi|rationale}}}} with custom text. The default rationale is {{samp|<nowiki>[[WP:DRAFTIFY|Not ready]]</nowiki> for mainspace, incubate in draftspace}} * '''Edit summary for preparing article for draftspace''': Add the line <code>var m2d_editsummary = "{{gi|editSummary}}";</code> to [[Special:MyPage/common.js|your common.js]], replacing {{kbd|{{gi|editSummary}}}} with custom text. The default rationale is {{samp|<nowiki>[[WP:AFC|AFC]] draft</nowiki>}} * '''Disable logging within your userspace''': Add the line <code>var m2d_doNotLog = {{gi|true}};</code> to [[Special:MyPage/common.js|your common.js]] to disable logging moves (in [[Special:MyPage/Draftify log|/Draftify log]] within your userspace). Logging is enabled if this is omitted or set to {{samp|false}}. * '''Contributor notification message''' *: {{red|'''Use with caution''', and only if you are sure what you're doing:}} *: Add the line <code>var m2d_notification = "{{gi|Your message here}}";</code> to [[Special:MyPage/common.js|your common.js]] *: '''Important:''' within your message: *: * You need to use <code>$1</code> instead of the page title *: * You need to prevent wikitext transformations on the common.js page itself (i.e. <code>\~~\~~</code> instead of <code><nowiki>~~~~</nowiki></code>, <code><nowiki>\{\{subst:</nowiki></code> instead of <code><nowiki>{{subst:</nowiki></code> for any template substitution) *: * You need to escape any quotation marks (<code>"</code> characters) as <code>\"</code> to avoid syntax errors. *: * Any actual backslashes (<code>\</code> characters) need to be escaped as <code>\\</code>. * '''Location of your log file''': You can change the location of the <code>Draftify_log</code> file by creating a redirect from <code>User:Username/Draftify_log</code> to whatever location you prefer (as long as it is within your userspace). fng3w0hulzf3q9hl54plbbfmjdtcqcd ಮುಂಬೈಡ್ ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಮೇಲೊಲು 0 27970 366100 365771 2026-08-21T02:26:03Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366100 wikitext text/x-wiki '''ಮುಂಬೈದ ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಗುಂಪು''' [[ಮುಂಬಯಿ|ಮುಂಬೈದ]] ಕೋಟೆದ ಪ್ರದೇಶೊಡು 19ನೆ ಶತಮಾನೊದ ವಿಕ್ಟೋರಿಯನ್ ಪುನರುತ್ಥಾನದ ಸಾರ್ವಜನಿಕ ಬೊಕ್ಕ 20ನೆ ಶತಮಾನೊದ ಮುಂಬೈ ಆರ್ಟ್ ಡೆಕೊ ಖಾಸಗಿ ಕಟ್ಟಡೊಲೆನ ಸಂಗ್ರಹ ಆದುಂಡು. <ref>{{Cite web|last=|first=|title=Victorian Gothic and Art Deco Ensembles of Mumbai|url=https://whc.unesco.org/en/list/1480/|accessdate=2023-08-21|website=UNESCO World Heritage Centre|language=en}}</ref> ಈ ಸಂಕೀರ್ಣೊನು ೨೦೧೮ಡ್ ಯುನೆಸ್ಕೋ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಘೋಷಣೆ ಮಲ್ತೆರ್. ಈ ಕಟ್ಟಡೊಲು ಓವಲ್ ದ ಸುತ್ತಲ ಉಂಡು, ಒಂಜಿ ಕಾಲೊಡು ಎಸ್ಪ್ಲಾನೇಡ್ ಪನ್ಪಿನ ಒಂಜಿ ಮಲ್ಲ ಮನರಂಜನೆದ ಪ್ರದೇಶ. ಓವಲ್ ದ ಪೂರ್ವ ಭಾಗೊನು ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಸಾರ್ವಜನಿಕ ಕಟ್ಟಡೊಲು ಸುತ್ತುವರಿದ್ ಉಂಡು, ಬೊಕ್ಕ ಪಶ್ಚಿಮ ಭಾಗೊನು ಬ್ಯಾಕ್ ಬೇ ರಿಕ್ಲಮೇಶನ್ ಬೊಕ್ಕ ಮರೈನ್ ಡ್ರೈವ್ ದ ಆರ್ಟ್ ಡೆಕೊ ಕಟ್ಟಡೊಲು ಸುತ್ತುವರಿದ್ ಉಂಡು. ಒಟ್ಟು 94 ಕಟ್ಟಡೊಲೆನ್ ರಕ್ಷಣೆ ಮಲ್ಪುನ ಉದ್ದೇಶ ಈ ನಾಮನಿರ್ದೇಶನೊಡು ಉಂಡು. [[ಫೈಲ್:Mumbai_Weltkulturerbe.svg|thumb|291x291px| ಕೆತ್ತೆದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣದ ಸ್ಥಳ ಬೊಕ್ಕ ವಿಸ್ತಾರ:]] ಓವಲ್ ದ ಪೂರ್ವೊಡು 19ನೆ ಶತಮಾನೊದ ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಕಟ್ಟಡೊಲು ಮುಖ್ಯವಾದ್ ಬಾಂಬೆ ಹೈಕೋರ್ಟ್, ಮುಂಬೈ ವಿಶ್ವವಿದ್ಯಾಲಯ (ಫೋರ್ಟ್ ಕ್ಯಾಂಪಸ್) ಬೊಕ್ಕ ಸಿಟಿ ಸಿವಿಲ್ ಬೊಕ್ಕ ಸೆಷನ್ಸ್ ಕೋರ್ಟ್ (ಪಿರಾಕ್ದ ಸೆಕ್ರೆಟೇರಿಯಟ್ ಕಟ್ಟಡೊಡು ಉಂಡು). <ref name="DCOfficialWeb">{{Cite web|title=History - City Civil and Sessions Court, Mumbai|url=http://ecourts.gov.in/mumbai/citycivil|accessdate=29 June 2015|publisher=District Courts of India}}</ref> <ref name="UNESCOPage">{{Cite web|title=The Victorian & Art Deco Ensemble of Mumbai - UNESCO World Heritage Centre|url=https://whc.unesco.org/en/tentativelists/5740/|archiveurl=https://web.archive.org/web/20180316011333/https://whc.unesco.org/en/tentativelists/5740/|archivedate=16 March 2018|accessdate=29 June 2015|publisher=UNESCO World Heritage Centre|url-status=bot: unknown}}<cite class="citation web cs1" data-ve-ignore="">. UNESCO World Heritage Centre. Archived from [https://whc.unesco.org/en/tentativelists/5740/ the original] on 16 March 2018<span class="reference-accessdate">. Retrieved <span class="nowrap">29 June</span> 2015</span>.</cite></ref> ಈ ಪ್ರದೇಶೊಡು ಮುಂಬೈದ ಗಡಿಯಾರ ಗೋಪುರೊಡು ಒಂಜಿ ಆಯಿನ ರಾಜಬಾಯಿ ಗಡಿಯಾರ ಗೋಪುರಲಾ ಉಂಡು. 20ನೆ ಶತಮಾನೊದ ಆರ್ಟ್ ಡೆಕೊ ಕಟ್ಟಡೊಲು ಓವಲ್ ದ ಪಶ್ಚಿಮ ಭಾಗೊಗು ಸುತ್ತಲ ಉಂಡು ಬೊಕ್ಕ ಅವು ಮುಖ್ಯವಾದ್ ಖಾಸಗಿ ಸ್ವಾಮ್ಯದ ವಸತಿ ಕಟ್ಟಡೊಲೆನ್ ಬೊಕ್ಕ ಎರೋಸ್ ಸಿನಿಮಾನ್ ದೆತೊಂದುಂಡು. ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಕಟ್ಟಡೊಲೆನ ಈ ಗುಂಪುನ್ ಜೂನ್ 30, 2018 ಗ್ ಬಹ್ರೇನ್ ದ ಮನಾಮಾಡ್ ನಡತಿನ ವಿಶ್ವ ಪರಂಪರೆ ಸಮಿತಿದ 42ನೇ ಅಧಿವೇಶನೊಡು ವಿಶ್ವಸಂಸ್ಥೆದ ಶೈಕ್ಷಣಿಕ, ವೈಜ್ಞಾನಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಸಂಸ್ಥೆದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣೊಲೆನ ಪಟ್ಟಿಡ್ ಸೇರ್ಪಾಯೆರ್ <ref>{{Cite web|date=30 June 2018|title=Four sites added to UNESCO’s World Heritage List|url=https://whc.unesco.org/en/news/1845/|accessdate=5 July 2018|publisher=UNESCO}}</ref> '''ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣೊಗು ಯುನೆಸ್ಕೋದ ಅನುಮೋದನೆ''' ಮಾನದಂಡ (ii): ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಒಟ್ಟುಗುಲು ಕಾಲಾನಂತರೊಡು ಯುರೋಪಿಯನ್ ಬೊಕ್ಕ ಭಾರತೀಯ ಮಾನವ ಮೌಲ್ಯೊಲೆನ ಮಹತ್ವದ ವಿನಿಮಯೊನು ತೋಜಾವುಂಡು. ಮಲ್ಲ ಸಾರ್ವಜನಿಕ ಕಟ್ಟಡೊಲೆನ ವಿಕ್ಟೋರಿಯನ್ ಸಭೆ ಗೋಥಿಕ್ ಪುನರುತ್ಥಾನದ ಅಂಶೊಲೆನ್ ಭಾರತೀಯ ಅಂಶೊಲೆನೊಟ್ಟುಗು ಮಿಶ್ರಣ ಮಲ್ಪುನ ಮೂಲಕ ಇಂಡೋ-ಗೋಥಿಕ್ ಶೈಲಿನ್ ಸೃಷ್ಟಿ ಮಲ್ತ್ಂಡ್, ಬಾಲ್ಕನಿಲು ಬೊಕ್ಕ ವೆರಾಂಡಾಲೆನ್ ಪರಿಚಯಿಸವುನ ಮೂಲಕ ಸ್ಥಳೀಯ ಹವಾಮಾನೊಗು ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ರೂಪಾಂತರೊಲೆನ್ ಮಲ್ತ್ಂಡ್. ಮುಂಬೈದ ಐಕಾನಿಕ್ ಸಿನಿಮಾ ಹಾಲ್ ಲೆನ ಆರ್ಟ್ ಡೆಕೊ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಕಟ್ಟಡೊಲು ಭಾರತೀಯ ವಿನ್ಯಾಸೊನು ಆರ್ಟ್ ಡೆಕೊ ಚಿತ್ರೊಲೆನ್ ಮಿಶ್ರಣ ಮಲ್ತ್ ದ್ ಇಂಡೋ-ಡೆಕೊ ಪನ್ಪಿನ ಒಂಜಿ ಅನನ್ಯ ಶೈಲಿನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಭಾರತದ ಉಪಖಂಡೊಡು ಉಂದೆತ ಪ್ರಭಾವ ವ್ಯಾಪಿಸಂಡ್. ಮಾನದಂಡ (iv): ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಗುಂಪುಲು ರಡ್ಡ್ ಶತಮಾನೊಡ್ದ್ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ನಗರ ಯೋಜನೆಡ್ ನಡತಿನ ಬೆಳವಣಿಗೆಲೆನ್ ಪ್ರತಿಬಿಂಬಿಸವುಂಡು. ಈ ರಡ್ಡ್ ಗುಂಪುಲು ವಾಸ್ತುಶಿಲ್ಪದ ಶೈಲಿಲೆನ್, ನಿರ್ಮಾಣ ಸಾಮಗ್ರಿಲು ಬೊಕ್ಕ ತಂತ್ರೊಲೆನ ಪ್ರಗತಿದ ಹಂತೊಲೆನ್, ನಗರ ಯೋಜನೆದ ತತ್ವಶಾಸ್ತ್ರೊಲೆನ್, ಬೊಕ್ಕ ಓವಲ್ ಮೈದಾನೊಲೆಡ್ ಅನನ್ಯ ಬೊಕ್ಕ ವ್ಯತಿರಿಕ್ತ ಐತಿಹಾಸಿಕ ಹಂತೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ರಡ್ಡ್ ಜಾತ್ರೆಲುಲಾ ಬಾಂಬೆದ ರಡ್ಡ್ ಪ್ರಮುಖ ನಗರ ವಿಸ್ತರಣೆಲೆನ ಸೃಷ್ಟಿ ಆತ್ಂಡ್, ಉಂದು ಇರುವನೆ ಶತಮಾನೊಡು ಬೊಕ್ಕ ಇತ್ತೆದ ಕಾಲೊಗು ನಗರೊದ ಅಭಿವೃದ್ಧಿಗ್ ಕಾರಣವಾತ್ಂಡ್. == ಯುನೆಸ್ಕೋದ ಪರಂಪರೆದ ರಚನೆಲೆನ ಅತ್ತಂದೆ ಜಾಗೆಲೆನ ಪಟ್ಟಿ == === ವಿಕ್ಟೋರಿಯನ್ === ==== ಭಾರತೀಯ ನವ-ಗೋಥಿಕ್ (ಹಿಂದೂ ಗೋಥಿಕ್) ವಾಸ್ತುಶಿಲ್ಪ ==== * ನಗರ ನಾಗರಿಕ ಬೊಕ್ಕ ಅಧಿವೇಶನ ನ್ಯಾಯಾಲಯ (ಹಳೆಯ ಕಾರ್ಯದರ್ಶಿ ಕಚೇರಿ) * ಬಾಂಬೆ ವಿಶ್ವವಿದ್ಯಾಲಯ ಆವರಣ: ೧. ** ರಾಜಬಾಯಿ ಗಡಿಯಾರ ಗೋಪುರ ** ವಿಶ್ವವಿದ್ಯಾಲಯದ ಗ್ರಂಥಾಲಯ ** ಕಾನ್ವೊಕೇಶನ್ ಸಭಾಂಗಣ * ಬೊಂಬಾಯಿ ಉಚ್ಚ ನ್ಯಾಯಾಲಯ * ಸಾರ್ವಜನಿಕ ಕಾಮಗಾರಿ ಇಲಾಖೆದ ಕಟ್ಟಡ * ಎಸ್ಪ್ಲಾನೇಡ್ ಮ್ಯಾನ್ಷನ್ * ಡೇವಿಡ್ ಸಸ್ಸೂನ್ ಗ್ರಂಥಾಲಯ * ಎಲ್ಫಿನ್ಸ್ಟೋನ್ ಕಾಲೇಜ್ * ಮಹಾರಾಷ್ಟ್ರ ಪೊಲೀಸ್ ಮುಖ್ಯ ಕಚೇರಿ * ಇಂಡಿಯನ್ ಮರ್ಕಂಟೈಲ್ ಮ್ಯಾನ್ಷನ್ ==== ನಿಯೋಕ್ಲಾಸಿಕಲ್ ==== * ಸ್ಟ್ಯಾಂಡರ್ಡ್ ಚಾರ್ಟರ್ಡ್ ಬ್ಯಾಂಕ್ ಬಿಲ್ಡಿಂಗ್ * ವೆಲ್ಲಿಂಗ್ಟನ್ ಫೌಂಟೇನ್ * ಸೈನ್ಯ ಬೊಕ್ಕ ನೌಕಾಪಡೆದ ಕಟ್ಟಡ * ವಿಜ್ಞಾನ ಸಂಸ್ಥೆ * ಸರ್ ಕೋವಾಸ್ಜಿ ಜಹಾಂಗೀರ್ ಹಾಲ್, ನ್ಯಾಷನಲ್ ಗ್ಯಾಲರಿ ಆಫ್ ಮೊಡರ್ನ್ ಆರ್ಟ್ ==== ಇಂಡೋ-ಸರಾಸೆನಿಕ್ ==== * ಪಶ್ಚಿಮ ರೈಲ್ವೆ ಮುಖ್ಯ ಕಚೇರಿ ಕಟ್ಟಡ * ಛತ್ರಪತಿ ಶಿವಾಜಿ ಮಹಾರಾಜ ವಾಸ್ತು ಸಂಗ್ರಹ * ಭವ್ಯ ಅಮ್ದಾರ್ ನಿವಾಸ್ === ಆರ್ಟ್ ಡೆಕೊ === * ರೀಗಲ್ ಸಿನಿಮಾ * ಎರೋಸ್ ಸಿನಿಮಾ * ಮೋಟಭೋಯ್ ಮ್ಯಾನ್ಷನ್ * ಸುನಾ ಮಹಲ್ * ಕೆವಾಲ್ ಮಹಲ್ * ಓವಲ್ ಗ್ರೌಂಡ್ ಬೊಕ್ಕ ಮರೈನ್ ಡ್ರೈವ್ ದ ಸುತ್ತಮುತ್ತ ಉಪ್ಪುನ ಕಟ್ಟಡೊಲು * ಮೆಟ್ರೊ ಇನಾಕ್ಸ್ ಸಿನಿಮೊಲು <gallery widths="220" heights="220"> ಫೈಲ್:Mumbai_03-2016_40_Bombay_High_Court.jpg|link=ಚಿತ್ರ:Mumbai_03-2016_40_Bombay_High_Court.jpg|alt=Bombay High Court (Neo-Gothic)| ಬೊಂಬಾಯಿ ಹೈಕೋರ್ಟ್ (ನವ-ಗೋಥಿಕ್) ಫೈಲ್:Mumbai_03-2016_39_University.jpg|link=ಚಿತ್ರ:Mumbai_03-2016_39_University.jpg|alt=Rajabai Clock Tower (Neo-Gothic)| ರಾಜಬಾಯಿ ಗಡಿಯಾರ ಗೋಪುರ (ನವ-ಗೋಥಿಕ್) ಫೈಲ್:Maharashtra_State_Police_Head_Quarters,_Fort,_Mumbai.jpg|link=ಚಿತ್ರ:Maharashtra_State_Police_Head_Quarters,_Fort,_Mumbai.jpg|alt=Maharashtra Police Headquarters (Neo-Gothic)| ಮಹಾರಾಷ್ಟ್ರ ಪೊಲೀಸ್ ಮುಖ್ಯ ಕಚೇರಿ (ನವಗೋಥಿಕ್) ಫೈಲ್:Standard_Chartered_Bank_Building_(Grindlays_Bank).jpg|link=ಚಿತ್ರ:Standard_Chartered_Bank_Building_(Grindlays_Bank).jpg|alt=Standard Chartered Bank Building (Neoclassical)| ಸ್ಟ್ಯಾಂಡರ್ಡ್ ಚಾರ್ಟರ್ಡ್ ಬ್ಯಾಂಕ್ ಬಿಲ್ಡಿಂಗ್ (ನಿಯೋಕ್ಲಾಸಿಕಲ್) ಫೈಲ್:Mumbai_-_Army_and_Navy_Building.jpg|link=ಚಿತ್ರ:Mumbai_-_Army_and_Navy_Building.jpg|alt=Army and Navy Building (Neoclassical)| ಸೈನ್ಯ ಬೊಕ್ಕ ನೌಕಾಪಡೆದ ಕಟ್ಟಡ (ನವಶಾಸ್ತ್ರೀಯ) ಫೈಲ್:Western_Railway_Headquarter_Building_at_Churchgate_in_Mumbai.jpg|link=ಚಿತ್ರ:Western_Railway_Headquarter_Building_at_Churchgate_in_Mumbai.jpg|alt=Western Railway Headquarters Building (Indo-Saracenic)| ಪಶ್ಚಿಮ ರೈಲ್ವೆ ಮುಖ್ಯ ಕಚೇರಿ ಕಟ್ಟಡ (ಇಂಡೋ-ಸಾರ್ಸೆನಿಕ್) ಫೈಲ್:Side_view_of_Eros_Cinema.jpg|link=ಚಿತ್ರ:Side_view_of_Eros_Cinema.jpg|alt=The Eros Cinema (Art Deco)| ದಿ ಎರೋಸ್ ಸಿನಿಮಾ (ಆರ್ಟ್ ಡೆಕೊ) </gallery> '''ವಿಶ್ವಕೋಶ ದಾಖಲೆ: ಮುಂಬೈದ ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಗುಂಪುಲು''' ಬಾಂಬೆದ ವಿಕ್ಟೋರಿಯನ್ ಗೋಥಿಕ್ ಬೊಕ್ಕ ಆರ್ಟ್ ಡೆಕೊ ಎನ್ಸೆಂಬಲ್ಸ್ ಪಂಡ ಮುಂಬೈದ ಫೋರ್ಟ್ ಪ್ರಿನ್ಸಿಪ್ಟ್ ಡ್ 19ನೆ ಶತಮಾನೊದ ವಿಕ್ಟೋರಿಯನ್ ರಿವೈವಲ್ ಸಾರ್ವಜನಿಕ ಬೊಕ್ಕ 20ನೆ ಶತಮಾನೊದ ಮುಂಬೈ ಆರ್ಟ್ ಡೆಕೊ ಖಾಸಗಿ ಕಟ್ಟಡೊಲೆನ ಸಂಗ್ರಹ. ಈ ಗುಂಪುನ್ 2018ಡ್ ಯುನೆಸ್ಕೋ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಘೋಷಣೆ ಮಲ್ತೆರ್.<ref>{{https://www.worldheritageexplorer.org/sites/victorian_gothic_and_art_deco_ensembles_of_mumbai.html}}</ref> == ಉಲ್ಲೇಖೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲು == # [https://www.flickr.com/photos/meckleychina/sets/72157620262447654/ ಬಾಂಬೆಡ್ ಆರ್ಟ್ ಡೆಕೊ ಕಟ್ಟಡೊಲು (ಫ್ಲಿಕ್ರ್ ಆಲ್ಬಮ್)] b51jd9ovgss1irfyogygmadha1ksg9s ಶಿಮ್ಲಾ 0 27983 366169 365831 2026-08-21T09:39:50Z Mahaveer Indra 1023 366169 wikitext text/x-wiki {{under construction}} '''ಶಿಮ್ಲಾ''' ಬಡಕಾಯಿ ಭಾರತದ [[ಹಿಮಾಚಲ ಪ್ರದೇಶ]] ರಾಜ್ಯದ ರಾಜಧಾನಿ ಬೊಕ್ಕ ಮಲ್ಲ ನಗರ. ಬ್ರಿಟಿಷೆರೆನ ಆಡಳಿತ ಇಪ್ಪನಗ ಶಿಮ್ಲಾ ೧೮೬೪ನೇ ಇಸವಿಡ್ ಭಾರತದ ಅರೆಗಾಲದ ರಾಜಧಾನಿ ಪಂದ್ ಘೋಷಣೆ ಮಲ್ತೆರ್. ಸ್ವತಂತ್ರ ಬತ್ತಿ ಬೊಕ್ಕ, ಈ ನಗರ ಪೂರ್ವ ಪಂಜಾಬ್‌ದ ರಾಜಧಾನಿ ಆದಿತ್ತ್ಂಡ್. ಇತ್ತೆ ಹಿಮಾಚಲ ಪ್ರದೇಶದ ರಾಜಧಾನಿ ಆದುಂಡು. ಉಂದು ಹಿಮಾಚಲ ಪ್ರದೇಶದ ಮಲ್ಲ ಬೊಕ್ಕ ಅಭಿವೃದ್ಧಿ ಆಯಿನ ನಗರ, ಅತ್ತಂದೆ ರಾಜ್ಯದ ಮುಖ್ಯ ವ್ಯಾಪಾರ, ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ಶಿಕ್ಷಣದ ಕೇಂದ್ರಲಾ ಅಂದ್.<ref>{{cite web|last=bedi|first=rahul|title=Steady Decline: From Simla To Shimla, And Now To Shyamala!|url=https://www.thecitizen.in/index.php/en/newsdetail/index/2/15315/steady-decline-from-simla-to-shimla-and-now-to-shyamala|access-date=27 December 2021|website=www.thecitizen.in|language=en-US|archive-date=27 December 2021|archive-url=https://web.archive.org/web/20211227210809/https://www.thecitizen.in/index.php/en/newsdetail/index/2/15315/steady-decline-from-simla-to-shimla-and-now-to-shyamala|url-status=dead}}</ref> ೧೮೧೫ನೇ ಇಸವಿಡ್ ಬ್ರಿಟಿಷೆರ್ ಈ ಜಾಗೆನ್ ತನ್ಕುಲೆ ವಶ ಮಲ್ತೊಂದೆರ್. ನೆಕ್ ದುಂಬು, ಮುಲ್ಪ ಎಲ್ಯ ಎಲ್ಯ ಹಳ್ಳಿಲು ಇತ್ತ್ಂಡ್ ಪಂದ್ ದಾಖಲೆ ಉಂಡು. ಮುಲ್ತ ಹವಾಮಾನ ಬ್ರಿಟಿಷೆರೆಗ್ ಇಷ್ಟ ಆಂಡ್, ಅಂಚಾದ್ ಹಿಮಾಲಯದ ದಟ್ಟ ಕಾಡ್‌ಡ್ ಈ ನಗರನ್ ಕಟ್ಯೆರ್. ಬ್ರಿಟೀಷೆರೆನ ಅರೆಗಾಲದ ರಾಜಧಾನಿಯಾದಿಪ್ಪನಗ, ಶಿಮ್ಲಾ ೧೯೦೬ದ ಸಿಮ್ಲಾ ಡೆಪ್ಯುಟೇಶನ್, ೧೯೧೪ದ ಸಿಮ್ಲಾ ಒಪ್ಪಂದ ಬೊಕ್ಕ ೧೯೪೫ದ ಸಿಮ್ಲಾ ಸಮ್ಮೇಳನ ಅತ್ತಂದೆ ಬೇತೆ ಬೇತೆ ಮುಖ್ಯ ರಾಜಕೀಯ ಸಬೆಕ್ಲು ಮಾತ ಮುಲ್ಪ ನಡತ್ತ. ನಮ ದೇಸೊಗು ಸ್ವಾತಂತ್ರ ಬತ್ತಿ ಬೊಕ್ಕ, ೨೮ ಸಂಸ್ಥಾನೊಲೆನ್ ಒಟ್ಟು ಸೇರಾದ್ ೧೯೪೮ಡ್ ಹಿಮಾಚಲ ಪ್ರದೇಶ ರಾಜ್ಯ ಉದಿತ್ ಬತ್ತ್ಂಡ್. ಸ್ವತಂತ್ರ ಬತ್ತಿ ಬೊಕ್ಕಲಾ, ಈ ನಗರ ಒಂಜಿ ಮುಖ್ಯ ರಾಜಕೀಯ ಕೇಂದ್ರವಾದ್ ಒರಿಂಡ್, ೧೯೭೨ನೇ ಇಸವಿದ ಸಿಮ್ಲಾ ಒಪ್ಪಂದ ಮುಲ್ಪ ಆಂಡ್. ಹಿಮಾಚಲ ಪ್ರದೇಶ ರಾಜ್ಯದ ಪುನರ್ವಿಂಗಡಣೆ ಆಯಿ ಬೊಕ್ಕ, ಅಲ್ಪದ ಇತ್ತಿನ ಮಹಾಸು ಜಿಲ್ಲೆಗ್ ಶಿಮ್ಲಾ ಪಂದ್ ಪುದರ್ ದೀಯೆರ್.<ref>{{cite web |title=History of Shimla Town |url=https://hpshimla.nic.in/history/ |website=hpshimla.nic.in |publisher=District Administration Shimla , Developed and hosted by National Informatics Centre |access-date=19 August 2026}}</ref> ಶಿಮ್ಲಾಡ್ ಬ್ರಿಟಿಷೆರೆ ಕಾಲೊದ ನಿಯೋ-ಗೋಥಿಕ್ ಶೈಲಿದ ಕಟ್ಟೊಣೊಲು, ಅತ್ತಂದೆ ಮಸ್ತ್ ದೇವಸ್ಥಾನೊಲು ಬೊಕ್ಕ ಚರ್ಚ್‌ಲು ಮಾತ ಉಲ್ಲ. ಈ ಪೇಂಟೆದ ನೈಸರ್ಗಿಕ ಪರಿಸರ ಪ್ರವಾಸಿ ಜನೊಕ್ಲೆನ ಮನಸ್ ಮರ್ಲ್ ಮಲ್ಪುಂಡು. ನಗರದ ಮುಖ್ಯ ಆಕರ್ಷಣೆಲು ಪಂಡ ಶ್ರೀ ಹನುಮಾನ್ ದೇಸ್ತಾನ, ವೈಸ್‌ರೀಗಲ್ ಲಾಡ್ಜ್, ಕ್ರೈಸ್ಟ್ ಚರ್ಚ್, ಮಾಲ್ ರೋಡ್, ದ ರಿಡ್ಜ್ ಬೊಕ್ಕ ಅನ್ನಾಡೇಲ್. ನಗರದ ಕೇಂದ್ರದ ಬಡಕಾಯಿಗ್ ಜಾಖು ಬೊಕ್ಕ ತೆಂಕಾಯಿಗ್ ಅನ್ನಾಡೇಲ್, ಮೂಡಾಯಿಗ್ ಸಂಜೌಲಿ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿಗ್ ಚೋಟಾ ಶಿಮ್ಲಾ ಉಂಡು. ಬ್ರಿಟಿಷೆರ್ ಕಟ್ಟಾದಿನ, ಯುನೆಸ್ಕೋ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಕಾಲ್ಕಾ-ಶಿಮ್ಲಾ ರೈಲ್ವೆ ಮಾರ್ಗಲಾ ಮುಖ್ಯ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆ ಆದುಂಡು.<ref>{{cite web |title=History of Shimla Town |url=https://hpshimla.nic.in/history/ |website=hpshimla.nic.in |publisher=District Administration Shimla , Developed and hosted by National Informatics Centre |access-date=19 August 2026}}</ref> ನೆತ್ತ ಗುಡ್ಡೆದ ಇಳಿಜಾರ್‌ದ ಜಾಗೆರ್ದಾತ್ರ, ೨೦೦೫ಡ್ ಶುರುವಾಯಿನ 'ಎಂಟಿಬಿ ಹಿಮಾಲಯ' ಪನ್ಪಿ ಬೈಕಿಂಗ್ ರೇಸ್ ಮುಲ್ಪ ನಡಪುಂಡು. ಉಂದು ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಮಲ್ಲ ಲೇಸ್ ಇನ್ಪಿ ಪುಗರ್ತೆ ಉಂಡು. ದಕ್ಷಿಣ ಏಷ್ಯಾದ ಮಲ್ಲ ನೈಸರ್ಗಿಕ ಐಸ್ ಸ್ಕೇಟಿಂಗ್ ಮೈದಾನ ಮುಲ್ಪ ಉಂಡು. ಪ್ರವಾಸೋದ್ಯಮ ಕೇಂದ್ರ ಅತ್ತಂದೆ, ಈ ನಗರ ಮಸ್ತ್ ಕಾಲೇಜುಲು ಬೊಕ್ಕ ಸಂಶೋಧನಾ ಸಂಸ್ಥೆಲು ಇಪ್ಪುನ ಒಂಜಿ ಶಿಕ್ಷಣದ ಕೇಂದ್ರಲಾ ಆತುಂಡು. ==ಪುದರ್== ==ಇತಿಹಾಸೊ== ==ಹವಾಗುಣ== ==ಬೌಗೋಳಿಕ ಮಾಹಿತಿ== ==ಜನಸಂಖ್ಯಾಶಾಸ್ತ್ರೊ== ==ಉಲ್ಲೇಕ== {{reflist}} knlub0sqrng4f7yf13ya81qypo5ixj7 ಲಿವಾಡಿಯಾ ಅರಮನೆ 0 27987 366103 365848 2026-08-21T02:41:58Z InternetArchiveBot 4316 Rescuing 2 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366103 wikitext text/x-wiki {{Short description|ಕ್ರಿಮಿಯಾದ ಲಿವಾಡಿಯಾದಲ್ಲಿರುವ ಅರಮನೆ}} {{Infobox building | name = ಲಿವಾಡಿಯಾ ಅರಮನೆ | native_name = Ливадийский дворец | native_name_lang = ru | image = Лівадійський палацовий комплекс7.jpg | image_caption = ಲಿವಾಡಿಯಾ ಅರಮನೆಯ ಮುಖಮಂಟಪ | building_type = ಅರಮನೆ | architectural_style = ನವ-ನವೋದಯ | location_city = ಲಿವಾಡಿಯಾ, ಕ್ರಿಮಿಯಾ | coordinates = {{coord|44|28|04|N|34|08|36|E|type:landmark|display=title,inline}} | material = ಬಿಳಿ ಕ್ರಿಮಿಯನ್ ಸುಣ್ಣದ ಕಲ್ಲು | architect = ನಿಕೊಲಾಯ್ ಕ್ರಾಸ್ನೋವ್ | completion_date = 1911 | number_of_rooms = 116 | website = [https://xn----7sbfcibirdakblh1d6aj3h.xn--p1ai/ ಲಿವಾಡಿಯಾ ಅರಮನೆಯ ಅಧಿಕೃತ ಜಾಲತಾಣ] | embed = {{Infobox historic site |embed = yes |designation1 = UKRAINE NATIONAL |designation1_offname = ಅರಮನೆಯ ಕಟ್ಟಡ ಸಂಕೀರ್ಣ |designation1_type = ವಾಸ್ತುಶಿಲ್ಪ |designation1_number = 0100115 }} }} [[Image:Внутренний дворик дворца.jpg|thumb|200px|ಲಿವಾಡಿಯಾ ಅರಮನೆದ ಒಂಜಿ ಇಟಾಲಿಯನ್ ಅಂಗಳ]] [[Image:Yalta-livadiya-crimea-palace-chapel-1.jpg|thumb|200px|ಚಾಪೆಲ್]] '''ಲಿವಾಡಿಯಾ ಅರಮನೆ''' ಕಡೆತ ರಷ್ಯಾದ ತ್ಸಾರ್, ನಿಕೋಲಸ್ II, ಬೊಕ್ಕ ಅರೆನ ಕುಟುಂಬದ ಲಿವಾಡಿಯಾ, ಕ್ರೈಮ್ ಡ್ ಇತ್ತಿನ ಒಂಜಿ ಬೇಸಿಗೆದ ಪಿರವುದ ಜಾಗೆ. ಯಾಲ್ಟಾ ಸಮ್ಮೇಳನ 1945ಡ್ ಅಲ್ಪ ನಡತ್ಂಡ್, ಅಪಗ ಅರಮನೆಡ್ ಫ್ರಾಂಕ್ಲಿನ್ ಡೆಲಾನೊ ರೂಸ್ವೆಲ್ಟ್ ಬೊಕ್ಕ ಅಮೆರಿಕದ ನಿಯೋಗದ ಇತರ ಸದಸ್ಯೆರೆನ ಅಪಾರ್ಟ್ಮೆಂಟ್ ಲು ಇತ್ತ್ಂಡ್ – ಸೋವಿಯೆತ್ ನಿಯೋಗ ಯುಸುಪೊವ್ ಅರಮನೆ (ಕ್ರೈಮಿಯಾ) ಡ್ ಇತ್ತ್ಂಡ್, ಬೊಕ್ಕ ಬ್ರಿಟಿಷೆರ್ ಯುಸುಪೊವ್ ಅರಮನೆ (ಕ್ರೈಮಿಯಾ) ಡ್ ಇತ್ತೆರ್, ಬೊಕ್ಕ ಬ್ರಿಟಿಷೆರ್ ಒಂಟೊವ್| ಅರಮನೆಡ್ ಸುಮಾರ್ ಎಡ್ಮ ಕಿಲೋಮೀಟರ್ ದೂರೊಡು ಉಂಡು. ಈ ಅರಮನೆಡ್ ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಉಂಡು, ಬೊಕ್ಕ ಕೆಲವೊಂಜಿ ಸರ್ತಿ ಅಂತರಾಷ್ಟ್ರೀಯ ಶೃಂಗಸಭೆಲೆಗ್ ಉಂದು ಉಪಯೋಗ ಆಪುಂಡು. ==ಇತಿಹಾಸ== ದುಂಬು ಲ್ಯಾಂಬ್ರೊಸ್ ಕ್ಯಾಟ್ಸೊನಿಸ್ ಗ್ ಮಂಜೂರು ಆಯಿನ ಬೊಕ್ಕ ಪೊಟೊಕಿ ಕುಟುಂಬದ ಸ್ವಾಧೀನ ಆಯಿನ ಲಿವಾಡಿಯಾ ಎಸ್ಟೇಟ್ 1860 ದಶಕೊಡು ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಕುಟುಂಬದ ಬೇಸಿಗೆ ನಿವಾಸ ಆಂಡ್, ಅಪಗ ವಾಸ್ತುಶಿಲ್ಪಿ ಇಪ್ಪೊಲಿಟೊ ಮೊನಿಘೆಟ್ಟಿ ಒಂಜಿ ಮಲ್ಲ ಅರಮನೆ, ಒಂಜಿ ಎಲ್ಯ ಅರಮನೆ, ಬೊಕ್ಕ ಒಂಜಿ ಎಲ್ಯ ಚರ್ಚ್ ನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ನಿವಾಸೊಗು ರಷ್ಯಾದ ಅಲೆಕ್ಸಾಂಡರ್ II ಆದಗ ಅದಗ ಬರೊಂದು ಉಪ್ಪುವೆರ್, ಆಂಡ ಅರೆನ ಉತ್ತರಾಧಿಕಾರಿ ರಷ್ಯಾದ ಅಲೆಕ್ಸಾಂಡರ್ III ಜೀವನ ಮಲ್ತೊಂದಿತ್ತೆರ್, ಬೊಕ್ಕ ಎಲ್ಯ ಅರಮನೆಡ್ ತೀರ್ ಪೋಯೆರ್. ಅರೆನ ಮಗೆ ನಿಕೋಲಸ್ II ಮಲ್ಲ ಅರಮನೆನ್ ಕೆತ್ತುದು ಮಲ್ಲ ರಚನೆನ್ ಮಲ್ಪೆರೆ ನಿರ್ಧರಿಸಯೆರ್. ಎಲ್ಯ ಮಲಿಜ್ (ಚಿಕ್ಕ) ಅರಮನೆನ್ ಅರೆನ ಅಪ್ಪೆನ ಸಾವುದ ಜಾಗೆ ಆದ್ ಸಂರಕ್ಷಣೆ ಮಲ್ತೆರ್, ಆಂಡ ನನೊಂಜಿ ವಿಶ್ವ ಯುದ್ಧೊಡು ಅವು ಪೂರಾ ನಾಶ ಆಂಡ್. ಸುಮಾರ್ ೧೯೦೯ಡ್, ​​ನಿಕೋಲಾಯ್ ಕ್ರಾಸ್ನೋವ್ (ವಾಸ್ತುಶಿಲ್ಪಿ), ಯಾಲ್ಟಾ ದ ಅತ್ಯಂತ ಫ್ಯಾಶನ್ ವಾಸ್ತುಶಿಲ್ಪಿ, ಕೊರೆಜ್ ಡ್ ಗ್ರ್ಯಾಂಡ್ ಡ್ಯೂಕಲ್ ನಿವಾಸೊಲೆಗ್ ಜವಾಬ್ದಾರರಾಯಿನ, ಒಂಜಿ ಪೊಸ ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಅರಮನೆದ ಯೋಜನೆಲೆನ್ ತಯಾರ್ ಮಲ್ಪೆರೆ ನಿಯೋಜಿಸಯೆರ್. ತ್ಸಾರ್ ನ ಡೈರಿ ಸೂಚಿಸವುಂಡು ಈ ವಿನ್ಯಾಸೊನು ಇಂಪೀರಿಯಲ್ ಫ್ಯಾಮಿಲಿಡ್ ಮಸ್ತ್ ಚರ್ಚೆ ಮಲ್ತೆರ್; ಅರಮನೆದ ನಾಲ್ ಮುಖಭಾಗೊಲು ಬೇತೆ ಬೇತೆ ಆವೊಡು ಪಂದ್ ನಿರ್ಧರಿಸಯೆರ್. ತಿಂಗೊಲು ನಿರ್ಮಾಣ ಆಯಿ ಬೊಕ್ಕ, ಪೊಸ ಅರಮನೆನ್ ೧೧ ಸೆಪ್ಟೆಂಬರ್ ೧೯೧೧ಡ್ ಉದ್ಘಾಟನೆ ಮಲ್ತೆರ್. ನವೆಂಬರ್ ಡ್ ರಷ್ಯಾದ ಗ್ರ್ಯಾಂಡ್ ಡಚೆಸ್ ಒಲ್ಗಾ ನಿಕೋಲೇವ್ನಾ ಲಿವಾಡಿಯಾಡ್ ತನ್ನ ೧೬ನೇ ಪುಟ್ಟು ಪರ್ಬೊನು ಆಚರಣೆ ಮಲ್ತೆರ್. ೧೯೧೭ಡ್ ಫೆಬ್ರವರಿ ಕ್ರಾಂತಿದ ಬೊಕ್ಕ, ನಿಕೋಲಸ್ ನ ಅಮ್ಮೆರ್, ಡೋವೇಜರ್ ಸಾಮ್ರಾಜ್ಞಿ ಮಾರಿಯಾ ಫಿಯೋಡೊರೊವ್ನಾ (ಡೆನ್ಮಾರ್ಕ್ ದ ಡಾಗ್ಮಾರ್), ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಕುಟುಂಬದ ಕೆಲವು ಸದಸ್ಯೆರೆನೊಟ್ಟುಗು ಲಿವಾಡಿಯಾಗ್ ಪಲಾಯನ ಮಲ್ತೆರ್. ಅಕುಲು ಕಡೆಕ್ ಬ್ರಿಟಿಷ್ ನೌಕೆ ಎಚ್ ಎಂಎಸ್ ಮಾರ್ಲ್ಬೊರೊ (೧೯೧೨) ಡ್ದ್ ರಕ್ಷಣೆ ಮಲ್ತೆರ್, ಉಂದೆನ್ ಡೋವೇಜರ್ ಸಾಮ್ರಾಜ್ಞಿನ ಸೋದರ ಮಗೆ, ರಾಜೆ ಜಾರ್ಜ್ ವಿ ಕಡಪುಡುವೆರ್. ೧೯೧೭ ರ ರಷ್ಯಾದ ಕ್ರಾಂತಿದ ನಂತರ, ಅರಮನೆನ್ ರಾಷ್ಟ್ರೀಕರಣ ಮಲ್ತೆರ್ ಬೊಕ್ಕ, ಆರ್ ಎಸ್ ಎಫ್ ಎಸ್ ಆರ್ ದ ಪಿಪಲ್ಸ್ ಕಮಿಷರ್ ಲೆನ ಕೌನ್ಸಿಲ್ ನ ಆದೇಶದ ಪ್ರಕಾರ “ಕಾರ್ಮಿಕೆರೆನ ಚಿಕಿತ್ಸೆಗಾದ್ ಕ್ರಿಮಿಯಾನ್ ಬಳಕೆ ಮಲ್ಪುನ ಬಗ್ಗೆ” ಲೆನಿನ್ ಡಿಸೆಂಬರ್ ೨೧೯೧೯ ಡ್ ಸಹಿ ಪಾಡ್ದ್ ಇತ್ತೆರ್ ,<ref>{{cite web |title=Декрет Совета народных комиссаров РСФСР от 21.12.1920 «Об использовании Крыма для лечения трудящихся» |url=https://krym.rusarchives.ru/dokumenty/dekret-soveta-narodnyh-komissarov-rsfsr-ob-ispolzovanii-kryma-dlya-lecheniya |website=Государственный архив Республики Крым |publisher=Российский государственный архив социально-политической истории |access-date=13 August 2025 |language=ru}}</ref>ಕೆಲಸಗಾರೆರೆಗಾತ್ರ ಒಂಜಿ ಸ್ಯಾನಿಟೋರಿಯಂ ಆದ್ ಪರಿವರ್ತಿಸಾಯೆರ್. ೧೯೨೫ ಡ್ದ್ ೧೯೩೧ ಮುಟ್ಟ ಉಂದು ಪೀಪಲ್ಸ್ ಕಮಿಷನರಿಟ್ ಆಫ್ ಹೆಲ್ತ್ ದ ಪೀಸೆಂಟ್ ಸ್ಯಾನಿಟೋರಿಯಂ ಆದ್ ಕಾರ್ಯನಿರ್ವಹಿಸೊಂದಿತ್ತ್ಂಡ್, ಕ್ಷಯರೋಗ ವಿರೋಧಿ ಬೊಕ್ಕ ಸಾಮಾನ್ಯ ಚಿಕಿತ್ಸಕ ಚಿಕಿತ್ಸೆಡ್ ವಿಶೇಷತೆನ್ ಪಡೆಯೊಂದಿತ್ತ್ಂಡ್.<ref>{{cite web |title=Historical Note |url=https://livadiapalace.crimea.ua/historical-note |website=Livadia Palace–Park Museum |publisher=Ministry of Culture of the Republic of Crimea |access-date=13 August 2025}}</ref><ref>{{cite web |title=Ливадия. Санаторий |url=https://prlib.ru/item/421067 |website=Presidential Library |publisher=Russian State Library |access-date=13 August 2025 |language=ru}}</ref> ೧೯೩೧ ಬೊಕ್ಕ ೧೯೪೧ ನಡುಟು ಉಂದು ಒಂಜಿ ಹವಾಮಾನ-ಚಿಕಿತ್ಸೆದ ಸಂಯೋಜನೆ ಬೊಕ್ಕ ಆರೋಗ್ಯ-ವಿಶ್ರಾಂತಿ ಸಂಕೀರ್ಣೊದ ರೂಪೊಡು ಕಾರ್ಯನಿರ್ವಹಿಸೊಂದಿತ್ತ್ಂಡ್. ಯುದ್ಧಕಾಲೊದ ಆಕ್ರಮಣ ಬೊಕ್ಕ ಹಾನಿ ಆಯಿ ಬೊಕ್ಕ, ಅರಮನೆನ್ ಪುನಃಸ್ಥಾಪನೆ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ಅಯಿತ ಸ್ಯಾನಿಟೋರಿಯಂ ಪಾತ್ರೊನು ಪುನರಾರಂಭ ಮಲ್ತೆರ್. ಕಟ್ಟಡೊದ ಕೆಲವು ಭಾಗೊಲೆನ್ 1974ಡ್ ಸಾರ್ವಜನಿಕೆರೆಗ್ ಕುಡ ತೂವರೆ ಬುಡಿಯೆರ್, ಬೊಕ್ಕ ಇತ್ತೆ ಉಂದು ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಆದ್ ಕಾರ್ಯನಿರ್ವಹಿಸೊಂದುಂಡು. ಕೆಲವು ಆಧುನಿಕ ವಾದೊಲೆಗ್ ವಿರುದ್ಧವಾದ್, ಅರಮನೆನ್ ಏಪಲಾ ಒಂಜಿ ಮನೋವೈದ್ಯಕೀಯ ಆಸ್ಪತ್ರೆ ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್ ಪನ್ಪಿನೆಕ್ ದಾಖಲೆ ಸಾಕ್ಷಿ ಇಜ್ಜಿ.<ref>{{cite book |last=Starks |first=Tricia |title=The Body Soviet: Propaganda, Hygiene, and the Revolutionary State |location=Madison |publisher=University of Wisconsin Press |year=2008 |isbn=9780299229641}}</ref> ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧೊದ ಸಮಯೊಡು, ಜರ್ಮನ್ ಕ್ರೈಮಿಯನ್ ಅಭಿಯಾನ (೧೯೪೧-೧೯೪೨) ನ್ ಯಶಸ್ವಿಯಾದ್ ಮುಗಿಪಾವುನ ಒಂಜಿ ಸಮಾರಂಭ, ಸೆವಾಸ್ಟೊಪೋಲ್ ದ ಮುತ್ತಿಗೆ (೧೯೪೧-೧೯೪೨) ೧೧ನೇ ಸೈನ್ಯ (ವರ್ಮಾ/| ಜನರಲ್ ಎರಿಚ್ ವಾನ್ ಮ್ಯಾನ್ಸ್ಟೈನ್, ಬೊಕ್ಕ ಮ್ಯಾನ್ಸ್ಟೈನ್ ನ ''ಜನರಲ್ಫೆಲ್ಡ್ಮಾರ್ಷಲ್'' ಸ್ಥಾನೊಗು ಬಡ್ತಿ ಮನ್ಪುನ ಕಾರ್ಯಕ್ರಮೊ, ಜುಲೈ ೬, ೧೯೪೨ಡ್ ಲಿವಾಡಿಯಾ ಅರಮನೆದ ಉದ್ಯಾನೊಡು ನಡತ್ಂಡ್. ಸೈನಿಕೆರ್, ಅಧಿಕಾರಿಲು ಭಾಗವಹಿಸಾಯೆರ್. ಜರ್ಮನ್ "ರಿಟ್ಟರ್ಕ್ರೂಜ್" (ನೈಟ್ಸ್ ಕ್ರಾಸ್) ಬೊಕ್ಕ "ಡಾಯ್ಚೆಸ್ ಕ್ರೂಜ್ ಇನ್ ಗೋಲ್ಡ್" (ಜರ್ಮನ್ ಕ್ರಾಸ್ ಇನ್ ಗೋಲ್ಡ್) ಪ್ರಶಸ್ತಿನ್ ಅಲ್ಪ ಪಡೆಯೆರ್.<ref>Book: Author: Erich von Manstein. Titel: [[Verlorene Siege]]. 2000. 16 Auflage. Bonn. pp. 283–85</ref> ಆ ಅರಮನೆ ಒಂಜಿ ಐತಿಹಾಸಿಕ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಆಂಡ್. ಐತಿಹಾಸಿಕ ಪೀಠೋಪಕರಣೊಲೆಡ್ ಬಹುಪಾಲು ಕಳೆವೊಂದು ಪೋತುಂಡು. ಆಗಸ್ಟ್ ೨೦೦೭ಡ್ ಈ ಅರಮನೆನ್ ಸೆವೆನ್ ವಂಡರ್ಸ್ ಆಫ್ ಉಕ್ರೇನ್ ಯೋಜನೆಡ್ ''ಆಧುನಿಕ ಇತಿಹಾಸದ ಮೈಲುಗಲ್ಲು'' ಪಂಡ್ದ್ ಗುರುತಿಸಾಯೆರ್. ಉಕ್ರೇನಿಯನ್ ಪಾಪ್ ಗಾಯಕಿ ಸೋಫಿಯಾ ರೊಟಾರು, ರಷ್ಯಾ, ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಮೊಲ್ಡೊವಾ ದೇಶೊಲೆನ ಅಧ್ಯಕ್ಷೆರೆನ ಒಟ್ಟುಗು ಅರಮನೆಡ್ ತನ್ನ 60ನೇ ಪುಟ್ಟು ಪರ್ಬೊನು ಆಚರಣೆ ಮಲ್ತೆರ್ - ಯಾಲ್ಟಾ ಸಮ್ಮೇಳನ ಡ್ದ್ ಬೊಕ್ಕ ಇಂಚಿನ ರಡ್ಡನೆ ಸಭೆ - 2008ಡ್ ಲಿವಾಡಿಯಾ ಅರಮನೆದ ಜೀರ್ಣೋದ್ಧಾರೊಗು ನಿಧಿಲಾ ಕೊರ್ತೆರ್.<ref>{{cite web |url=http://www.tden.ru/articles/024175/ |title=Sofia Rotaru decided to take Livadia Palace in charge |work=Твой День |access-date=2007-12-28 |archive-url=https://web.archive.org/web/20080511075849/http://www.tden.ru/articles/024175/ |archive-date=2008-05-11 |url-status=dead}}</ref> ಅರಮನೆಡ್ ಅಸಂಖ್ಯಾತ ಶೈಕ್ಷಣಿಕ ಸಮ್ಮೇಳನೊಲು ನಡತ್ಂಡ್. ಅರಮನೆದ ಸಿಬ್ಬಂದಿಲು ಅಕ್ಲೆನ ಸಂಶೋಧನೆನ್ ಸಕ್ರಿಯವಾದ್ ಪ್ರಕಟಿಸಾವೆರ್.<ref>{{cite web |url=http://www.livadia-palace.crimea.com/publishing.htm |title=Ливадийский дворец-музей. Издательская деятельность |website=www.livadia-palace.crimea.com |access-date=12 January 2022 |archive-url=https://web.archive.org/web/20130522133112/http://www.livadia-palace.crimea.com/publishing.htm |archive-date=22 May 2013 |url-status=dead}}</ref> ತೂವರೆ ಬತ್ತಿನಂಚಿನ ಜನಕುಲು ೧೯೪೫ ದ ಘಟನೆಲೆಡ್ ಸೈತಿನಕುಲೆನ ಬಗ್ಗೆ ವಿಶೇಷವಾದ್ ಆಸಕ್ತಿ ತೋಜಾವೆರ್.<ref>{{Cite web|url=http://krim-konference.at.ua/index/conference_yalta_45_13/0-5|title = КРЫМСКАЯ КОНФЕРЕНЦИЯ 1945 г. (альманах) - Conference "YALTA-45/13"}}</ref> ಜಾರ್ ಅಲೆಕ್ಸಾಂಡರ್ III ನ ಸಮಾಧಿದ 123ನೇ ವಾರ್ಷಿಕೋತ್ಸವೊಡು, 2017 ನವೆಂಬರ್ 18 ತಾರೀಕ್ ಗ್, ರಷ್ಯಾದ ಅಧ್ಯಕ್ಷೆ ವ್ಲಾಡಿಮಿರ್ ಪುಟಿನ್ ಲಿವಾಡಿಯಾ ಅರಮನೆದ ಮೈದಾನೊಡು ಅಲೆಕ್ಸಾಂಡರ್ III ಗ್ ಒಂಜಿ ಸ್ಮಾರಕೊನು ಸಮರ್ಪಣೆ ಮಲ್ತೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== ಲಿವಾಡಿಯಾ ಅರಮನೆನ್ ನವ-ನವೋದಯ ಶೈಲಿಡ್ ಬಿಳುಪುದ ಕ್ರಿಮಿಯನ್ ಸುಣ್ಣದ ಕಲ್ಲ್ ಡ್ದ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಕಟ್ಟಡೊಡು ಕ್ಯಾರಾರಾ ಅಮೃತಶಿಲೆದ ಕಮಾನುದ ಮುಖಮಂಟಪ, ಒಂಜಿ ವಿಶಾಲವಾದ್ ಅರೇಬಿಕ್ ಪ್ಯಾಟಿಯೊ, ಒಂಜಿ ಇಟಾಲಿಯನ್ ಪ್ಯಾಟಿಯೊ, ಒಂಜಿ ಫ್ಲೋರೆಂಟೈನ್ ಗೋಪುರ, ಅಲಂಕೃತ ಬ್ರಾಮಂಟೆ ಸ್ಕ್ಯೂ ಕಿಟಕಿಲು, ಒಂಜಿ "ಬಾಲ್ಕನಿ-ಬೆಲ್ವೆಡೆರೆ", ಬೊಕ್ಕ ಜಾಸ್ಪರ್ ಹೂದಾನಿಲೆನೊಟ್ಟುಗು ಬಹು ಕೊಲ್ಲಿಲು ಉಂಡು. ಒಂಜಿ ಗ್ಯಾಲರಿ ಅರಮನೆನ್ 1866ಡ್ ಮೊನಿಘೆಟ್ಟಿ ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಒಂಜಿ ನವ-ಬೈಜಾಂಟೈನ್ ಚರ್ಚ್ ದ ಎಕ್ಸಾಲ್ಟೇಶನ್ ಆಫ್ ದ ಕ್ರಾಸ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು. ಈ ಅರಮನೆಡ್ 116 ಕೊಠಡಿಲು ಉಂಡು, ಒಳಾಂಗಣೊಲು ಬೇತೆ ಬೇತೆ ಶೈಲಿಡ್ ಸಜ್ಜು ಆತ್ಂಡ್. ಒಂಜಿ ಪೊಂಪಿಯನ್ ವೆಸ್ಟಿಬ್ಯೂಲ್, ಒಂಜಿ ಇಂಗ್ಲಿಷ್ ಬಿಲಿಯರ್ಡ್-ರೂಮ್, ಒಂಜಿ ಬರೊಕ್ ರಿವೈವಲ್ ಆರ್ಕಿಟೆಕ್ಚರ್/ನಿಯೋ-ಬರೊಕ್ ಡೈನಿಂಗ್ ರೂಮ್, ಬೊಕ್ಕ ಒಂಜಿ ಫ್ರಾನ್ಸಿಸ್-ಹೊನೊರೆ-ಜಾರ್ಜೆಸ್ ಜಾಕೋಬ್-ಡೆಸ್ಮಾಲ್ಟರ್/ಜಾಕೊಬ್-ಶೈಲಿ ಮೇಪಲ್ ಮರತ ಅಧ್ಯಯನ ಉಂಡು, ಉಂದು ನಿಕೋಲಸ್ II ನ ವಿಶೇಷ ಮೆಚ್ಚುಗೆನ್ ಪುಟ್ಟಾದುಂಡು. ==ಚಿತ್ರೊಪುಟೊಲು== <gallery> File:Смерть Александра III в Ливадии. Худ.М.Зичи.1895 г..png|ರಷ್ಯಾದ ಅಲೆಕ್ಸಾಂಡರ್ III ಅರೆನ ಬೊಡೆದಿನೊಟ್ಟುಗು, ೧೮೯೫ File:Yalta Conference (Churchill, Roosevelt, Stalin) (B&W).jpg|ಫೆಬ್ರವರಿ ೧೯೪೫ಡ್ ಯಾಲ್ಟಾ ಸಮ್ಮೇಳನ </gallery> ==ಉಂದೆನುಲಾ ತೂಲೆ== * ಜಾರ್ನ ಮಾರ್ಗ, ಅರಮನೆದ ಸಂಕೀರ್ಣೊದ ಮಿತ್ತ್ ಪೋಪುಂಡು * ದುಲ್ಬೆರ್, ಅದೇ ವಾಸ್ತುಶಿಲ್ಪಿ ವಿನ್ಯಾಸಗೊಳಿಸಾಯಿನ ಸಮೀಪದ ಅರಮನೆ * ಯುಸುಪೊವ್ ಅರಮನೆ (ಕ್ರೈಮಿಯಾ), ಬೇತೆ ಒಂಜಿ ಸಮೀಪದ ಅರಮನೆ * ವೊರೊಂಟ್ಸೊವ್ ಅರಮನೆ (ಅಲುಪ್ಕಾ), ನೆರೆಕರೆತ ಅಲೂಪ್ಕಾಡ್ ==ಉಲ್ಲೇಕೊಲು== {{Reflist}} ==ಬಾಹ್ಯ ಕೊಂಡಿಲು== {{Commons category|Livadiya palace}} * [https://web.archive.org/web/20150704000801/http://livadia.ircha.net/ Livadia Palace photo album] {{in lang|en}} * [https://web.archive.org/web/20150315004722/http://worldwalk.info/en/catalog/294 Livadia Palace (Yalta)] * [http://krym.sarov.info/dvorets_livadija.html History and architecture] {{in lang|ru}} * [https://livadia.narod.ru/rus/excursion.html History and views of the palace] {{in lang|ru}} * [https://www.otdihinfo.ru/photo/294.html Photo (1024x768)] * [http://krim-konference.at.ua/index/conference_yalta_45_13/0-5 Conference devoted to Yalta-45] gpluwrym7om054w49laazd48i2e02i1 ಅಂಡಮಾನ್ ಬೊಕ್ಕ ನಿಕೋಬಾರ್ ದ್ವೀಪೊಲು 0 28017 366168 365966 2026-08-21T09:39:02Z Mahaveer Indra 1023 366168 wikitext text/x-wiki {{under construction}} '''ಅಂಡಮಾನ್ ಬೊಕ್ಕ ನಿಕೋಬಾರ್ ದ್ವೀಪೊಲು''' ನಮ ದೇಸೊದ [[ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಲು|ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ]]ಲೆಡ್ ಒಂಜಿ. ನೆಟ್ಟ್ ಒಟ್ಟು 836 ದ್ವೀಪೊಲು ಉಲ್ಲ. ಅವೆಟ್ 31 ದ್ವೀಪೊಲೆಡ್ ಮಾತ್ರ ಜನಕುಲು ವಾಸ ಮಲ್ತೊಂದುಲ್ಲೆರ್.<ref>{{Cite news |title=Andaman Nicobar News: Public Invited to Name 586 Islands |url=https://www.deccanherald.com/india/andaman-and-nicobar-islands/andaman-nicobar-administration-seeks-suggestions-from-public-for-naming-586-uninhabited-islands-3833952 |work=Deccan Herald |agency=PTI |date=17 December 2025 |access-date=25 May 2026 |archive-date=18 January 2026 |archive-url=https://web.archive.org/web/20260118174244/https://www.deccanherald.com/india/andaman-and-nicobar-islands/andaman-nicobar-administration-seeks-suggestions-from-public-for-naming-586-uninhabited-islands-3833952 |url-status=live }}</ref> ಅಂಡಮಾನ್ ಬುಕ್ಕ ನಿಕೋಬಾರ್‌ನ್ ರಡ್ಡ್ ಗುಂಪುಲಾದ್ ವಿಂಗಡಿಸದೆರ್. ಅವು- ಬಡಕಾಯಿದ ಅಂಡಮಾನ್ ದ್ವೀಪೊಲು ಬೊಕ್ಕ ತೆನ್ಕಾಯಿದ ನಿಕೋಬಾರ್ ದ್ವೀಪೊಲು. ಈ ರಡ್ಡ್ ದ್ವೀಪೊಲ್ನ ನಡುಟು 150 ಕಿ.ಮೀ ಅಗೆಲದ 'ಟೆನ್ ಡಿಗ್ರಿ ಚಾನೆಲ್' ಉಂಡು. ಅಂಡಮಾನ್ ನಿಕೋಬಾರ್‌ದ ರಾಜಧಾನಿ ಶ್ರೀ ವಿಜಯ ಪುರಂ (ಪರ ಪುದರ್ ಪೋರ್ಟ್ ಬ್ಲೇರ್). ಉಂದು ಈ ದ್ವೀಪೊದ ಮಾಮಲ್ಲ ಪೇಂಟೆಲಾ ಅಂದ್. ಶ್ರೀ ವಿಜಯಪುರಮ್ ಭಾರತದ ಚೆನ್ನೈಡ್ದ್ ಸುಮಾರು 1,190 ಕಿ.ಮೀ ಬೊಕ್ಕ ಕೋಲ್ಕತ್ತಾಡ್ದ್ 1,255 ಕಿ.ಮೀ ದೂರೊಡು ಉಂಡು. ಈ ದ್ವೀಪೊಲು ಪಡ್ಡಾಯಿಡ್ ಬಂಗಾಳ ಕೊಲ್ಲಿ ಬೊಕ್ಕ ಮೂಡಾಯಿಡ್ ಅಂಡಮಾನ್ ಸಮುದ್ರದ ನಡುಟು ಉಂಡು. ಗ್ರೇಟ್ ನಿಕೋಬಾರ್‌ದ ತೆನ್ಕಾಯಿ ಕೊಡಿಟ್ ಉಪ್ಪುನ ಇಂದಿರಾ ಪಾಯಿಂಟ್, ಭಾರತದ ಅತ್ಯಂತ ತೆನ್ಕಾಯಿದ ಕೊಡಿಯಾದುಂಡು. ಅಂಡಮಾನ್ ಬೊಕ್ಕ ನಿಕೋಬಾರ್‌ದ ಒಟ್ಟು ಇಸ್ತೀರ್ನೊ ೮೨೪೯ ಚದರ ಕಿಲೋಮೀಟರ್ ಉಂಡು. ಅಂಚನೆ ಮುಲ್ಪದ ಒಟ್ಟು ಜನಸಂಕ್ಯೆ ೨೦೧೧ನೇ ಇಸವಿದ ಕಾನೇಸ್ಮಾರಿದ ಪ್ರಕಾರ ೩,೮೦,೫೮೧ ಉಂಡು. ಅಂಡಮಾನ್ ನಿಕೋಬಾರ್‌ಡ್ದ್ ೧೮೫ ಕಿಲೋಮೀಟರ್ ದೂರೊಡು [[ಇಂಡೋನೇಷ್ಯಾ]], ೨೮೦ ಕಿಲೋಮೀಟರ್ ದೂರೊಡು [[ಮ್ಯಾನ್ಮಾರ್|ಮಯನ್ಮಾರ್]], ೬೫೦ ಕಿಲೋಮೀಟರ್ ದೂರೊಡು ಥಾಯ್ಲ್ಯಾಂಡ್ ದೇಸೊಲು ಉಲ್ಲ. ಅತ್ತಂದೆ ಈ ದೇಸೊಲೊಟ್ಟುಗು ಸಮುದ್ರಗಡಿನ್ ಪಟ್ಟೋಂದುಂಡು. ಆಡಳಿತಾತ್ಮಕವಾದ್ ಅಂಡಮಾನ್ ನಿಕೋಬಾರ್ ದ್ವೀಪೊಲೆನ್ ೩ ವಿಬಾಗ ಮಲ್ತ್‌ದೆರ್. ಅವು ಪನ್ನಗ- ನಿಕೋಬಾರ್ ಜಿಲ್ಲೆ, ಬಡಕಾಯಿ ಅಂಡಮಾನ್ ಜಿಲ್ಲೆ ಬುಕ್ಕ ತೆಂಕಾಯಿ-ಮದ್ಯೊದ ಅಂಡಮಾನ್ ಜಿಲ್ಲೆ. ಮುಲ್ಪದ ಮೂಲ ಅಂಡಮಾನಿ ಜನಕುಲು 30,000 ವರ್ಸೊಗ್ ದುಂಬು, ಮಧ್ಯ ಶಿಲಾಯುಗದ ಪೊರ್ತುಡ್ದೇ ಮುಲ್ಪ ಇತ್ತೆರ್ ಪಂಡ್‍ದ್ ಐತಿಹಾಸಿಕ ಅಧ್ಯಯನೊಡು ತೆರಿದ್ ಬರ್ಪುಂಡು. ಮುಲ್ಪದ ಇತಿಹಾಸ 2,200 ವರ್ಸೊ ಮುಟ್ಟ ಪಿರ ಪೋಪುಂಡು. 11ನೇ ಶತಮಾನೊಡು, ತಮಿಳು ಅರಸೆರ್ ಆಯಿನ ಚೋಳೆರ್, ಆಗ್ನೇಯ ಏಷ್ಯಾಡ್ ದಂಡಯಾತ್ರೆ ಮಲ್ಪರೆಗಾತ್ರ ಈ ದ್ವೀಪೊಲೆನ್ ಒಂಜಿ ನೌಕಾನೆಲೆತ ಲೆಕ ಗಳಸೊಂದಿತ್ತೆರ್. 1755ಡ್ ಈ ದ್ವೀಪೊಗು ಬತ್ತಿನ ಸುರುತ ಯುರೋಪಿಯನ್ನೆರ್ ಪಂಡ ಡ್ಯಾನಿಶ್ ಈಸ್ಟ್ ಇಂಡಿಯಾ ಕಂಪೆನಿ. 1868ದ ಪೊರ್ತುಡು ಈ ದ್ವೀಪೊಲು ಬ್ರಿಟಿಷ್ ಆಡಳಿತದ ಒಂಜಿ ಭಾಗ ಆಂಡ್. ರಡ್ಡನೇ ಮಹಾಯುದ್ಧದ ಪೊರ್ತುಡು, ಜಪಾನ್ ಈ ದ್ವೀಪೊಲೆನ ಮಿತ್ತ್ ದಾಳಿ ಮಲ್ತ್‌ದ್ ವಶಮಲ್ತೊಂಡ್. 1947ಡ್ ಭಾರತೊಗು ಸ್ವಾತಂತ್ರ್ಯ ತಿಕ್ಕಿಬೊಕ್ಕ, ಈ ಪ್ರದೇಶ ಭಾರತದ ಒಂಜಿ ಪ್ರಾಂತ್ಯ ಆಂಡ್. ಐಡ್ದ್ ಬೊಕ್ಕ 1950ಡ್ ಭಾರತದ ಸಂವಿಧಾನ ಜಾರಿಗ್ ಬತ್ತಿಬೊಕ್ಕ ಇಂದೆನ್ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶವಾದ್ ಗುರ್ತಿಸಯೆರ್.<ref>{{cite web |title=Historical Background of Andaman |url=https://southandaman.nic.in/history/ |website=southandaman.nic.in |publisher=Deputy commissioner Office ,South Andaman , Developed and hosted by National Informatics Centre |access-date=20 August 2026}}</ref> ==ಪುದರ್== ==ಇತಿಹಾಸೊ== ==ಹವಾಗುಣ== ==ಬೌಗೋಳಿಕ ಮಾಹಿತಿ== ==ಜನಸಂಕ್ಯಾ ಶಾಸ್ತ್ರೊ== ==ಉಲ್ಲೇಕ== {{reflist}} pazkp7y32c7we4hxjzjfalw7rbiiie6 ಧೋಲಾವಿರ 0 28018 366184 365931 2026-08-21T10:14:14Z Shreelatha.Halemane 6351 366184 wikitext text/x-wiki [[File:DHOLAVIRA SITE (24).jpg|thumb|DHOLAVIRA SITE (24)]] ಧೋಲಾವಿರ ಪಂಡ ಪಶ್ಚಿಮ ಭಾರತೊದ [[ಗುಜರಾತ್]] ರಾಜ್ಯೊದ [[ಕಚ್]] ಜಿಲ್ಲೆದ ಭಾಚೌ ತಾಲೂಕುದ ಖಾದಿರ್ಬೆಟ್ ಡ್ ಉಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಜಾಗೆ, ಉಂದೆತ ಪುದರ್ ದಕ್ಷಿಣೊಡು 1 ಕಿಲೋಮೀಟರ್ (0.62 ಮೈಲ್) ದೂರೊಡು ಉಪ್ಪುನ ಇತ್ತೆದ ಗ್ರಾಮೊದ ಪುದರ್ ಡ್ ಉಂಡು. ಈ ಊರು ರಾಧನ್ಪುರ್ ಡ್ದ್ 165 ಕಿ.ಮೀ (103 ಮೈಲ್) ದೂರೊಡು ಉಂಡು. ಸ್ಥಳೀಯವಾದ್ ಕೋಟದ ಟಿಂಬಾ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಈ ಜಾಗೆಡ್ ಪ್ರಾಚೀನ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ನಗರೊದ ಅವಶೇಷೊಲು ಉಂಡು. ಭೂಕಂಪೊಲು ಧೋಲಾವಿರ ಪನ್ಪಿನ ಈ ಜಾಗೆಗ್ ಸುಮಾರ್ ಸರ್ತಿ ಅಯಿತ ಕೆಟ್ಟ ಪರಿಣಾಮೊನ್ ಬೀರ್ದ್ಂಡ್ .<ref>https://www.thehindu.com/society/ruins-on-the-tropic-of-cancer/article19284214.ece</ref> ==ಜಾಗೆ== ಧೋಲಾವಿರ ಪನ್ಪಿನ ಜಾಗೆ ಉಷ್ಣವಲಯೊಡು(Tropic of cancer) ಉಂಡು. ಉಂದು ಐನ್ ಮಲ್ಲ ಹರಪ್ಪನ್ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಬೊಕ್ಕ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆಗ್ ಸೇರ್ದಿನ ಭಾರತೊದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಪಿರಾಕ್ದ ತಾಣ. ಉಂದು ಕಚ್ ಮರುಭೂಮಿದ ವನ್ಯಜೀವಿ ಅಭಯಾರಣ್ಯೊಡು ಕಚ್ ಗ್ರೇಟ್ ರನ್ ಡ್ ಖಾದಿರ್ ಬೆಟ್ ದ್ವೀಪೊಡು ಉಂಡು. ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಚತುರ್ಭುಜಾಕೃತಿದ ನಗರ ಉತ್ತರೊಡು ಮನ್ಸಾರ್ ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹಾರ್ ಪನ್ಪಿನ ಸುದೆತ್ತ ನಡುಟು ಉಂಡು. ಕ್ರಿ.ಪೂ ೧೪೫೦; ಆಂಡಲಾ, ಇತ್ತೆದ ಸಂಶೋಧನೆ ಸುಮಾರ್ ಕ್ರಿ.ಪೂ.೩೫೦೦ಡ್ (ಹರಪ್ಪನ್ ಪೂರ್ವ) ಆಕ್ರಮಣದ ಸುರುತ ಬೊಕ್ಕ ಕ್ರಿ.ಪೂ ೧೮೦೦ (ಹರಪ್ಪನ್ ಕಾಲೊದ ಸುರುತ ಭಾಗ) ಮುಟ್ಟ ನಿರಂತರತೆನ್ ಸೂಚಿಸವುಂಡು ==ಅನ್ವೇಷಣೆ== ಈ ಜಾಗೆನ್ ಸುರುಕು ಧೋಲಾವಿರ ಗ್ರಾಮದ ನಿವಾಸಿ ಶಂಭೂದನ್ ಗಾಧವಿ 1960 ದ ಸುರುತ ಭಾಗೊಡು ಕಂಡು ಕನತ್ದ್, ಸರ್ಕಾರದ ಗಮನೊನು ಈ ಜಾಗೆಗ್ ಕನರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. ಈ ಜಾಗೆನ್ 1967 ಅತ್ತ್ಂಡ 1968ಡ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ದ ಜೆ.ಪಿ.ಜೋಶಿ "ಅಧಿಕೃತವಾದ್" ಬೊಳ್ಪುಗು ಕನತೆರ್, ಬೊಕ್ಕ ಉಂದು ಎಣ್ಮ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಐನ್ ನೇ ಮಲ್ಲ ಜಾಗೆ. ಉಂದು ೧೯೯೦ಡ್ದ್ ಎಎಸ್ಐಡ್ದ್ ಉತ್ಖನನೊಡು ಉಂಡು, ಉಂದು "ಧೋಲಾವಿರ ಸಿಂಧು ಕಣಿವೆದ ನಾಗರಿಕತೆದ ಅರ್ಥ ಮಲ್ಪುನಗ ನಿಜವಾದ್ ಲಾ ಪೊಸ ಆಯಾಮೊಲೆನ್ ಸೇರ್ಸಾದ್ಂಡ್" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ತೋಜಾದ್ಂಡ್. ಧೋಲಾವಿರಾನ್ ಕಚ್ ದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಮೆಗಾಲಿಥಿಕ್(ಶಿಲಾಯುಗ) ತಾಣೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಂಡುಂಡು, ಆಂಡ ಕಚ್ಚ್ ಡ್ ತೆರಿನ ಒಂಜೇ ಒಂಜಿ ಮೆಗಾಲಿಥಿಕ್ ತಾಣ ಧನೆತಿ ಗ್ರಾಮದ ಸಮೀಪೊಡು ಉಂಡು. ಇತ್ತೆ ಮುಟ್ಟ ತೋಜುದು ಬತ್ತಿನ ಬೇತೆ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ತಾಣೊಲು ಹರಪ್ಪಾ, [[ಮೊಹೆಂಜೊದಾರೊ,]] ಗಣೇರಿವಾಳ, ರಾಖಿಗಡಿ, ಕಾಳಿಬಂಗನ್, ರೂಪನಗರ ಬೊಕ್ಕ ಲೋಥಲ್. ಜುಲೈ ೨೭, ೨೦೨೧ಡ್ ಧೋಲಾವಿರ: ಒಂಜಿ ಹರಪ್ಪನ್ ನಗರ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಪುದರ್ ದೀಯೆರ್ ==ಉತ್ಖನನೊಲು== ಬಿಷ್ಟ್ ನ ನಿರ್ದೇಶನೊಡು ಎಎಸ್ಐ 1989ಡ್ ಉತ್ಖನನ ಸುರು ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ 1990 ಬೊಕ್ಕ 2005 ನಡುಟು 13 ಕ್ಷೇತ್ರ ಉತ್ಖನನೊಲು ನಡತ್ಂಡ್. ಉತ್ಖನನ ನಗರ ಯೋಜನೆ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಬೊಲ್ಪುಗು ಕನೆತೆರ್, ಬೊಕ್ಕ ಪ್ರಾಣಿಲೆನ ಮೂಳೆಲು, ಚಿನ್ನ, ಬೊಳ್ಳಿ, ಟೆರ್ನಾಕೊಟರ್, ಬೊಕ್ಕ ಕಂಚಿದಂಚಿನ ಮಸ್ತ್ ಸಂಖ್ಯೆಡ್ ಪ್ರಾಚೀನ ವಸ್ತುಲೆನ್ ಕಂಡು ಕನತ್ಂಡ್. ದಕ್ಷಿಣ ಗುಜರಾತ್, ಸಿಂಧ್ ಬೊಕ್ಕ ಪಂಜಾಬ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿ ಏಷ್ಯಾದ ವಸತಿ ಸ್ಥಾನೊಲೆನ ನಡುಟು ಧೋಲಾವಿರ ಒಂಜಿ ಪ್ರಮುಖ ವ್ಯಾಪಾರ ಕೇಂದ್ರ ಆದಿತ್ತ್ಂಡ್ ಪಂದ್ ಪುರಾತತ್ತ್ವಜ್ಞೆರ್ ನಂಬುವೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== [[ಲೋಥಲ್]] ಬಂದರ್-ನಗರೊಡ್ದ್ ಪಿರಾಕ್ ದವು ಪನ್ಪಿನ ಅಂದಾಜಿಡ್, ಧೋಲಾವಿರಾ ನಗರೊಗು ಆಯತಾಕಾರೊದ ಆಕಾರೊ ಬೊಕ್ಕ ಸಂಘಟನೆ ಉಂಡು, ಬೊಕ್ಕ ಉಂದು 22 ಹೆಕ್ಟೇರ್ (54 ಎಕ್ರೆ) ಡ್ ವ್ಯಾಪಿಸಾಯಿನವು. ಈ ಪ್ರದೇಶೊದ ಉದ್ದ 771.1 ಮೀ (2,530 ಅಡಿ), ಬೊಕ್ಕ ಅಗಲ 616.85 ಮೀ (2,023.8 ಅಡಿ) ಉಂಡು. ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊದಾರೊದಲೆಕ ಅತ್ತ್, ಈ ನಗರೊನು ಮೂಜಿ ವಿಭಾಗೊಲೆನ್ ಸೇರ್ದಂಚಿನ ಒಂಜಿ ದುಂಬೇ ಇತ್ತಿನ ಜ್ಯಾಮಿತೀಯ ಯೋಜನೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ - ಮಿತ್ತ ಕೋಟೆ,ನಡುತ್ತ ಊರು, ಬೊಕ್ಕ ತಿರ್ತದ ಊರು. ಅಕ್ರೊಪೊಲಿಸ್ ಬೊಕ್ಕ ನಡುತ್ತ ಊರು ಅಕ್ಲೆನ ಸ್ವಂತ ರಕ್ಷಣಾ ಕೆಲಸ, ದ್ವಾರೊಲು, ನಿರ್ಮಾಣ ಆಯಿನ ಪ್ರದೇಶೊಲು,ಸಾದಿ ವ್ಯವಸ್ಥೆ,ಗುವೆಲ್ ಬೊಕ್ಕ ಮಲ್ಲ ತೆರೆದಿಪ್ಪುನ ಜಾಗೊಲೆನ್ ಸಜ್ಜು ಮಲ್ತೆರ್.ಉಂದೆಟ್ ನೈಋತ್ಯ ವಲಯೊದ ಪ್ರಮುಖ ಭಾಗೊನು ಸ್ವಾಧೀನ ಮಲ್ಪುಂಡು. ಎತ್ತರದ "ಕೋಟೆ" ನಿಲೆಲೆನ್ ಡಬಲ್ ರಾಂಪಾರ್ಟ್ ಲೆಡ್ ರಕ್ಷಣೆ ಮಲ್ಪುಂಡು. ಉಂದೆತ ಬರಿಟ್ 'ಬೈಲಿ' ಪನ್ಪಿನ ಜಾಗೆ ಉಂಡು ಅಲ್ಪ ಪ್ರಮುಖ ಅಧಿಕಾರಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಸಾಮಾನ್ಯ ಕೋಟೆಲೆನ ಉಳಯಿ ಉಪ್ಪುನ ನಗರ ಪ್ರದೇಸೊ 48 ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಉಂಡು. ಕೋಟೆದ ವಸಾಹತುಗು ಅವಿಭಾಜ್ಯವಾದ್ ಉಪ್ಪುನ ವಿಸ್ತಾರವಾದ್ ರಚನೆ-ಬೇರಿಂಗ್ ಪ್ರದೇಶೊಲು ಉಂಡು. ಗೋಡೆಲೆನ ಮಿತ್ತ್, ಬೇತೆ ಒಂಜಿ ವಸಾಹತು ತೋಜಿದ್ಬತ್ತ್ಂಡ್. ನಗರೊದ ಅತ್ಯಂತ ಗಮನಾರ್ಹ ಲಕ್ಷಣ ಪಂಡ ಅಯಿತ ಮಾತಾ ಕಟ್ಟಡೊಲು, ಕನಿಷ್ಠ ಅಯಿತ ಇತ್ತೆದ ಸಂರಕ್ಷಣೆದ ಸ್ಥಿತಿಡ್, ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್, ಆಂಡ ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊ-ದಾರೊ ಸೇರ್ದ್ ಬೇತೆ ಹರಪ್ಪನ್ ತಾಣೊಲು ಮಾತಲಾ ಇಟ್ಟಿಗೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಧೋಲಾವಿರಾ ರಡ್ಡ್ ತುಫಾನ್ ನೀರ್ದ ಕಾಲುವೆಲೆಡ್ದ್ ಸುತ್ತುವರಿದ್ ಉಂಡು; ಉತ್ತರೊಡು ಮನ್ಸರ್, ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹರ್. ಊರುದ ಚೌಕೊಡು, ನೆಲತ ಮಿತ್ತ್ ಎತ್ತರೊಡು ಒಂಜಿ ಜಾಗೆ ಉಂಡು, ಅವೆನ್ "ಕೋಟೆ" ಪನ್ಪೆರ್.<ref>https://web.archive.org/web/20170812062930/https://archaeologynewsnetwork.blogspot.com/2010/06/rise-and-fall-of-harappan-city.html</ref> ==ಜಲಾಶಯೊಲು== ಎಎಸ್ಐ ದ ಜಂಟಿ ಮಹಾನಿರ್ದೇಶಕೆರಾದ್ ನಿವೃತ್ತರಾಯಿನ ಬಿಷ್ತ್ ಪನ್ಪೆರ್, "ನೀರ್ ದ ಸಂರಕ್ಷಣೆ, ಕೊಯ್ಲು ಬೊಕ್ಕ ಸಂಗ್ರಹೊಗು ಅಭಿವೃದ್ಧಿ ಮಲ್ತಿನ ಧೋಲಾವಿರದ ಹರಪ್ಪನೆರೆನ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಸ್ಥೆ ಅಕ್ಲೆನ ಸುಧಾರಿತ ಹೈಡ್ರಾಲಿಕ್ ಎಂಜಿನಿಯರಿಂಗ್ ದ ಬಗ್ಗೆ ಪಾತೆರುಂಡು, ಕ್ರಿ.ಪೂ. ಮೂಜಿನೆ ಸಹಸ್ರಮಾನೊಡು ತಂತ್ರಜ್ಞಾನದ ಸ್ಥಿತಿನ್ ತೂಂಡ ಡೊಲಾವಿರಾದ ಸೋಫ್ಯಾಸ್ಟಿಕ್ ನೀರ್ ದ ಅನನ್ಯ ಲಕ್ಷಣೊಲು ಒಂಜಿ." ಚಾನೆಲ್ ಬೊಕ್ಕ ಜಲಾಶಯೊಲೆನ ಸಂರಕ್ಷಣೆದ ವ್ಯವಸ್ಥೆ, ಜಗತ್ತ್ಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಸುರುತ ವ್ಯವಸ್ಥೆ, ಉಂದು ಪೂರ್ತಿ ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ನಗರೊಡು ಮಲ್ಲ ಮಲ್ಲ ಜಲಾಶಯೊಲು ಇತ್ತ್ಂಡ್, ಅವುಲು ಮೂಜಿ ಜಲಾಶಯೊಲು ಬಹಿರಂಗ ಆತ್ಂಡ್. ಅವು ರಡ್ಡ್ ಗಿಟ್ಟದ ಕೆರೆಕುಲೆಡ್ದ್ ಬೇತೆ ಬೇತೆ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಬರ್ಸೊಡ್ದ್ ಬತ್ತಿನ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಆಪುಂಡು. ಉಂದು ಸ್ಪಷ್ಟವಾದ್ ಕಚ್ ದ ಮರುಭೂಮಿದ ಹವಾಮಾನ ಬೊಕ್ಕ ಪರಿಸ್ಥಿತಿಲೆಗ್ ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ಬತ್ತ್ಂಡ್, ಅಲ್ಪ ಕೆಲವು ವರ್ಷೊಲು ಬರ್ಸ ಬರ್ಪುಜಿ. ಈ ಜಾಗೆದ ಸಮೀಪೊಡು ಉತ್ತರ-ದಕ್ಷಿಣ ದಿಕ್ಕ್ ಡ್ ಪೋಪಿನ ಒಂಜಿ ಋತುಕಾಲಿಕ ನೀರ್ ನ್ ನೀರ್ ಸಂಗ್ರಹ ಮಲ್ಪರೆ ಕೆಲವು ಕಡೆಟ್ ಅಣೆಕಟ್ಟ್ ಮಲ್ತೆರ್. ೧೯೯೮ಡ್, ಈ ಜಾಗೆಡ್ ಬೇತೆ ಒಂಜಿ ಜಲಾಶಯೊನು ಕಂಡು ಕನತ್ ದೆರ್.<ref>https://www.cntraveller.in/magazine-story/india-via-gujarat/</ref> ಧೋಲಾವಿರಾದ ನಿವಾಸಿಲು ಮೂಜಿನೆ ಹಂತೊಡು ಬೇತೆ ಬೇತೆ ಗಾತ್ರೊದ ಪದಿನಾಜಿ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ಜಲಾಶಯೊಲೆನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಅವುಲು ಕೆಲವು ಮಲ್ಲ ವಸಾಹತುದ ತಿರ್ತ್ ನೆಲತ ಇಳಿಜಾರು ಪ್ರದೇಸೊದ ಲಾಭ ದೆತೊನುವೆರ್ ಅವುಲು ಗುವೆಲ್ದಂಚಿನ ರಚನೆ ಮಲ್ಪುವೆರ್.. ಬೇತೆ ಜಲಾಶಯೊಲೆನ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಕೆಲವು ಕಲ್ಲ್ಡ್ ಕಟ್ಟ್ದೆರ್ . ಇತ್ತೆದ ಕೆಲಸೊಡು ರಡ್ಡ್ ಮಲ್ಲ ಜಲಾಶಯೊಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್, ಒಂಜಿ ಕೋಟೆದ ಪೂರ್ವೊಡ್ ಬೊಕ್ಕೊಂಜಿ ಅಯಿತ ತೆಂಕ್ ದಿಕ್ಕ್ ಗ್ ಅನೆಕ್ಸ್ ದ ಸಮೀಪೊಡು. ಜಲಾಶಯೊಲು ಕಲ್ಲ್ ಡ್ದ್ ಲಂಬವಾದ್ ಕಡ್ತ್ ದ್ ಉಪ್ಪುಂಡು, ಬೊಕ್ಕ ಅವು ಸುಮಾರ್ 7 ಮೀ (23 ಅಡಿ) ಆಳೊಡು ಬೊಕ್ಕ 79 ಮೀ (259 ಅಡಿ) ಉದ್ದೊಡು ಉಪ್ಪುಂಡು. ಅವು ನಗರೊನು ಸುತ್ತುವರಿದ್ ಉಪ್ಪುಂಡು, ಆಂಡ ಕೋಟೆ ಬೊಕ್ಕ ಮೀಪುನ ಸ್ನಾನಘಟ್ಟಡ್ ಕೇಂದ್ರೊಡು ಎತ್ತರದ ನೆಲಟ್ ಉಪ್ಪುಂಡು. ಒಂಜಿ ಮಲ್ಲ ಗೂವೆಲ್ ಲಾ ಉಂಡು, ಉಂದೆಟ್ ಒಂಜಿ ಕಲ್ಲ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ಟ್ರಫ್ ನ್ ಒಂಜಿ ಡ್ರೈನ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು, ಉಂದು ಒಂಜಿ ಟ್ಯಾಂಕ್ ಗ್ ನೀರ್ ನ್ ಕನಪುಂಡು.ಮೀಪುನ ಜಾಗೆಗ್ ಬರ್ಪಿನವುಲು ಮೆಟ್ಟಿಲುಲು ಉಪ್ಪುಂಡು.<ref>https://whc.unesco.org/en/tentativelists/5892/</ref> ಅಕ್ಟೋಬರ್ ೨೦೧೪ಡ್, ಒಂಜಿ ಆಯತಾಕಾರದ ಮುಟ್ಟ್ ಉಪ್ಪುನ ಗುವೆಲ್ಡ್ ಉತ್ಖನನ ಸುರು ಆಂಡ್, ಉಂದು ೭೩.೪ ಮೀ (೨೪೧ ಅಡಿ) ಉದ್ದ, ೨೯.೩ ಮೀ (೯೬ ಅಡಿ) ಅಗಲ, ಬೊಕ್ಕ ೧೦ ಮೀ (೩೩ ಅಡಿ) ಆಳೊಡು ಇತ್ತ್ಂಡ್, ಉಂದು ಮೊಹೆಂಜೇದಾರೊದ ಮಲ್ಲ ಸ್ನಾನಘಟ್ಟೊಡ್ದ್ ಮೂಜಿ ಪಾಲ್ ಮಲ್ಲವು. ==ಮುದ್ರೆ ಮಲ್ಪುನ== ಧೋಲಾವಿರಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಕೆಲವು ಮುದ್ರೆಲು, ಸ್ಟೇಜ್ III ಗ್ ಸೇರ್ದಿನವು, ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಮಾತ್ರ ಮೂಲು ತೂವೋಲಿ ಅತ್ತಂದೆ, ಒವ್ವೆ ರೀತಿದ ಲಿಪಿ ಇಜ್ಜಿ. ಈ ರೀತಿದ ಮುದ್ರೆಲು ಸಿಂಧು ಮುದ್ರೆ ತಯಾರಿಕೆದ ಪಿರಾಕ್ ದ ಸಂಪ್ರದಾಯೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ==ಬೇತೆ ರಚನೆಲು ಬೊಕ್ಕ ವಸ್ತುಲು== ==ಪೂರ್ವೊದ ದ್ವಾರ== ಆ ಜಾಗೆಡ್ ಒಂಜಿ ಮಲ್ಲ ವೃತ್ತಾಕಾರದ ರಚನೆ ಒಂಜಿ ಸಮಾಧಿ ಅತ್ತಂಡ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ನಂಬುನವು ತಿಕ್ಕ್ಂಡ್, ಆಂಡಲಾ ಅಯಿಟ್ ಅಸ್ಥಿಪಂಜರೊಲು ಅತ್ತಂಡ ಬೇತೆ ಮಾನವ ಅವಶೇಷೊಲು ಇಜ್ಜಿ. ಈ ರಚನೆಡ್ ಒಂಜಿ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ಆಕಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಪತ್ತ್ ರೇಡಿಯಲ್ ಮಣ್ಣ್-ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು ಉಂಡು. ಫಾಲಸ್ ಎರೆಕ್ಟಸ್ ಆಂಡ ತರೆ ಬೊಕ್ಕ ಕಾರ್ ಪಾದೊದ ತಿರ್ತ್ ಕತ್ತರಿಸಾಯಿನ ಒಂಜಿ ಪುರುಷನ ಮೃದುವಾಯಿನ ಮರಳುಕಲ್ಲುದ ಶಿಲ್ಪೊನು ಪೂರ್ವ ದ್ವಾರೊದ ಮಾರ್ಗೊಡು ತೂಯೆರ್. ಮಸ್ತ್ ಶವಸಂಸ್ಕಾರದ ರಚನೆಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ (ಒಂಜಿ ಬುಕ್ಕೊ ಮಾತಾ ಅಸ್ಥಿಪಂಜರೊಲು ಇಜ್ಜಿ), ಅಂಚನೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಟೆರಾಕೋಟಾ ಮುದ್ರೆಲು, ಬಳೆ, ಉಂಗುರೊಲು, ಮಣಿ, ಬೊಕ್ಕ ಕೆತ್ತನೆ. ಡಾನ್ಸಿಂಗ್ ಗರ್ಲ್ ಪಂಡ ಹರಪ್ಪನ್ ನಾಗರಿಕತೆದ ಅತ್ಯಂತ ಪ್ರತಿಷ್ಠಿತ ಕಲಾಕೃತಿಲೆಡ್ ಒಂಜಿ, ಮೊಹೆಂಜೊದಾರೊಡು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಮಾರ್ ಕ್ರಿ.ಪೂ. 2500 ಡ್ದ್ ದುಂಬುದ ಈ ಕಂಚಿದ ಪ್ರತಿಮೆ ಸುಮಾರ್ 10.5 ಸೆಂಟಿಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು ಬೊಕ್ಕ ಒರಿ ಜವನೆರೆನ ನರ್ತಕಿನ ಕ್ರಿಯಾತ್ಮಕ ಶಕ್ತಿ ಬೊಕ್ಕ ಅನುಗ್ರಹೊನು ಸ್ಪಷ್ಟವಾದ್ ಸೆರೆಪತ್ತ್ದ್ಂಡ್. ==ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು== [[File:Cicular houses Dholavira.jpg|thumb|Circular houses of Dholavira - an ancient Harappan City]] ಧೋಲಾವಿರಾಡ್ ಏಳ್ ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲೆನ್ ತೂಯೆರ್, ಅವುಲು ರಡ್ಡ್ ನ್ ವಿವರವಾದ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಅವು ಮಲ್ಲ ಕಲ್ಲ್ ಕತ್ತರಿಸಾಯಿನ ಕೋಣೆಲೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಯೋಜನೆನ್ ಪಡೆಯಿನವು, ಅವು ಮಲ್ಲ ಅರ್ಧಗೋಳಾಕಾರದ ಎತ್ತರದ ಮಣ್ಣ್ ದ ಇಟ್ಟಿಗೆದ ನಿರ್ಮಾಣೊಲು. ಉತ್ಖನನ ಮಲ್ತಿನ ಒಂಜಿ ರಚನೆನ್ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೂಪೊಡು ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕೊಂಜಿಲಾ ಅದೇ ರೀತಿಡ್ ಡಿಸೈನ್ ಮಲ್ತೆರ್, ಆಂಡ ಸ್ಪೋಕ್ಸ್ ಇಜ್ಜಂದಿನ ಚಕ್ರೊದಲೆಕ. ಅವುಲು ಕುಂಬಾರಿಕೆದ ಸಮಾಧಿದ ವಸ್ತುಲು ಇತ್ತ್ಂಡಲಾ, ಒಂಜಿ ಸಮಾಧಿನ್ ಬುಡ್ದು ಬೇತೆ ಒವ್ವೆ ಅಸ್ಥಿಪಂಜರೊಲು ತೋಜಿಜಿ, ಅಲ್ಪ ಒಂಜಿ ಅಸ್ಥಿಪಂಜರ ಬೊಕ್ಕ ಒಂಜಿ ತಾಮ್ರದ ಕನ್ನಡಿ ಉಂಡು. ಒಂಜಿ ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಡ್ ಒಂಜಿ ತಾಮ್ರದ ತಂತಿಗ್ ಕೊಕ್ಕೆಲೆನ್ ಕಟ್ಟ್ ದ್ ದೀತಿನ ಸ್ಟೀಟೈಟ್ ಮಣಿಲೆನ ಕಂಕಣ, ಒಂಜಿ ಬಂಗಾರ್ದ ಬಳೆ, ಬಂಗಾರ್ ಬೊಕ್ಕ ಬೇತೆ ಮಣಿಕುಲುಲಾ ಒಂಜಿ ಅರ್ಧಗೋಳದ ರಚನೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಉತ್ಖನನೊನು ನಡಪಾಯಿನ ಭಾರತದ ಪುರಾತತ್ವ ಸಮೀಕ್ಷೆ, ಈ "ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಲು ಪಿರಾಕ್ದ ಬೌದ್ಧ ಸ್ತೂಪೊಲೆನ್ ನೆನಪು ಮಲ್ಪುಂಡು" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ಪನ್ಪುಂಡು. ಬೊಕ್ಕ "ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ಬೊಕ್ಕ ಅನ್ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೀತಿದ ವಿನ್ಯಾಸ ಶತಪಥ ಬ್ರಾಹ್ಮಣ ಬೊಕ್ಕ ಶುಲ್ಬ ಸೂತ್ರೊಡು ನಮೂದಿಸಾಯಿನ ಸರರತ-ಚಕ್ರ-ಸಿಟಿ ಬೊಕ್ಕ ಸಪ್ರಧಿ-ರತ-ಚಕ್ರ-ಸಿಟಿನ್ ನೆಂಪು ಮಲ್ಪುಂಡು" ==ಉತ್ತರೊದ ದ್ವಾರ== [[File:Dholavira-1.jpg|thumb|Dholavira-1]] ಸಿಂಧು ಲಿಪಿತ ಮಿತ್ತ್ ಕಪ್ಪು ಬಣ್ಣದ ಕುಂಬಾರಿಕೆ, ಚೌಕ ಮುದ್ರೆದ ಮುದ್ರೆಲು, ಸಿಂಧು ಲಿಪಿ ಇಜ್ಜಂದಿನ ಮುದ್ರೆಲು, ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದೊದ ಒಂಜಿ ಮಲ್ಲ ಸೈನ್ ಬೋರ್ಡ್, ಸಿಂಧು ಲಿಪಿತ ಪತ್ತ್ ಅಕ್ಷರೊಲೆನ್ ಸೇರ್ದುಂಡು. ಕಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಒಂಜಿ ಕಳಪೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಕುಲ್ಲುನ ಪುರುಷ ರೂಪೊನುಲಾ ತೂಯೆರ್, ಉಂದು ಹರಪ್ಪಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಉನ್ನತ ಗುಣಮಟ್ಟದ ರಡ್ಡ್ ಕಲ್ಲ್ ದ ಶಿಲ್ಪೊಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಮುಳ್ಳುದ ನೆಲತ ಮಲ್ಲ ಕಪ್ಪು ಜಾರ್ ಲೆನ್ ಲಾ ಈ ಜಾಗೆಡ್ ತೂಯೆರ್. ಒಂಜಿ ಮಲ್ಲ ಕಂಚಿದ ಸುತ್ತಿಗೆ, ಒಂಜಿ ಮಲ್ಲ ಉಳಿ, ಒಂಜಿ ಕಂಚಿನ ಕೈಟ್ ಪತ್ತೊಂದಿಪ್ಪುನ ಕನ್ನಡಿ, ಒಂಜಿ ಬಂಗಾರ್ದ ತಂತಿ, ಬಂಗಾರ್ದ ಕೆಬಿ ಓಲೆ, ತಾಮ್ರದ ಸೆಲ್ಟ್ಸ್ ಬೊಕ್ಕ ಚಿಪ್ಪುದ ಬಳೆ, ಕಲ್ಲ್ ದ ಫಾಲಸ್ ಲೆಕೊದ ಚಿಹ್ನೆಲು, ಸಿಂಧು ಶಾಸನ ಬೊಕ್ಕ ಚಿಹ್ನೆಲೆನ್ ದೀದ್ ಚೌಕ ಮುದ್ರೆಲು, ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಮುದ್ರೆ, ಕುಂಟುದ ಪ್ರಾಣಿಲು, ಪಾಟರ್ ಪೇಂಟೆಡ್ ಪಾಟರ್ಡ್, ಗೊಬ್ಬುಲು, ಡಿಶ್-ಆನ್-ಸ್ಟ್ಯಾಂಡ್, ರಂದ್ರೊಡುಪ್ಪುನ ಜಾರ್ ಲು, ಎಡ್ಡೆ ಆಕಾರೊಡು ಉಪ್ಪುನ ಟೆರಾಕೊಟಾ ಟಂಬ್ಲರ್ ಲು,ಕಡೆಪ್ಪುನ ಕಲ್ಲ್, ಗಾರೆಲು ಇಂಚಿನವುಲಾ ಈ ಜಾಗೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಬೇತೆ ಬೇತೆ ಅಳತೆದ ಕಲ್ಲ್ ದ ತೂಕೊಲೆನ್ ಲಾ ತೂಯೆರ್. ==ಕರಾವಳಿ ಮಾರ್ಗ== ಲೋಥಲ್ ಬೊಕ್ಕ ಧೋಲಾವಿರಾನ್ ಮಕ್ರಾನ್ ಕರಾವಳಿಡ್ ಸುತ್ಕಗನ್ ದೋರ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುನ ಒಂಜಿ ಕರಾವಳಿ ಮಾರ್ಗ ಉಂಡು. ==ಬಾಸೆ ಬೊಕ್ಕ ಲಿಪಿ == ಹರಪ್ಪನೆರ್ ಒಂಜಿ ಅಜ್ಞಾತ ಬಾಸೆನ್ ಪಾತೆರೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಅಕ್ಲೆನ ಲಿಪಿನ್ ಇತ್ತೆಲಾ ಅರ್ಥ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಉಂದೆಟ್ ಸುಮಾರ್ 400 ಮೂಲ ಚಿಹ್ನೆಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ನಂಬಿಕೆ ಉಂಡು, ಉಂದೆಟ್ ಮಸ್ತ್ ಬದಲಾವಣೆಲು ಉಂಡು. ಚಿಹ್ನೆಲು ಪದೊಕುಲೆಗ್ ಬೊಕ್ಕ ಉಚ್ಚಾರೊಲೆಗ್ ನಿಲೆಯಾದಿತ್ತ್ ದ್ ಉಪ್ಪೊಲಿ. ಬರವುದ ದಿಕ್ಕು ಸಾಮಾನ್ಯವಾದ್ ಬಲತ್ತ ಕಡೆಟ್ದ್ ಎಡತ್ತ್ ಗ್ ಇತ್ತ್ಂಡ್. ಹೆಚ್ಚಿನ ಶಾಸನೊಲು ಮುದ್ರೆಲೆಡ್ (ಬಹುತೇಕ ಕಲ್ಲ್ ಡ್ದ್ ಮಲ್ಪುನವು) ಬೊಕ್ಕ ಮುದ್ರೆಲೆಡ್ (ಮಣ್ಣ್ ದ ತುಂಡುಲೆಡ್ ಮುದ್ರೆನ್ ಒತ್ತುದು ಅಯಿತ ಗುರುತುನ್ ಬುಡ್ಪೆರ್) ಉಪ್ಪುಂಡು. ಕೆಲವು ಶಾಸನೊಲು ತಾಮ್ರದ ಫಲಕೊಲೆಡ್, ಕಂಚಿದ ಉಪಕರಣೊಲೆಡ್, ಬೊಕ್ಕ ಟೆರಾಕೊಟಾ, ಕಲ್ಲ್ ಬೊಕ್ಕ ಫೇಯೆನ್ಸ್ ಡ್ ಮಲ್ತಿನ ಎಲ್ಯ ವಸ್ತುಲೆಡ್ಲಾ ತೋಜುಂಡು. ಈ ಮುದ್ರೆಲೆನ್ ವ್ಯಾಪಾರೊಡು ಬೊಕ್ಕ ಅಧಿಕೃತ ಆಡಳಿತ ಕೆಲಸೊಗುಲಾ ಬಳಕೆ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಮೊಹೆಂಜೊದಾರೊ ಬೊಕ್ಕ ಬೇತೆ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ಜಾಗೆಲೆಡ್ ಮಸ್ತ್ ಕೆತ್ತನೆದ ಸಾಮಗ್ರಿಲೆನ್ ತೂಯೆರ್.<ref>https://web.archive.org/web/20110903114242/http://asi.nic.in/asi_exca_2007_dholavira.asp</ref> ==ಸೈನ್ ಬೋರ್ಡ್== [[File:The 'Ten Indus Scripts' discovered near the northern gateway of the Dholavira citadel.jpg|thumb|Ten Indus characters from the northern gate of Dholavira, dubbed the Dholavira Signboard, one of the longest known sequences of Indus characters]] ಧೋಲಾವಿರೊಡು ಮಸ್ತ್ ಮಹತ್ವದ ಅನ್ವೇಷಣೆಲು ನಗರೊದ ಉತ್ತರ ದ್ವಾರೊದ ಒಂಜಿ ಸೈಡ್ ರೂಮ್ ಡ್ ನಡತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಧೋಲಾವಿರ ಸೈನ್ಬೋರ್ಡ್ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ಹರಪ್ಪೆರ್ ಖನಿಜ ಜಿಪ್ಸಮ್ ದ ತುಂಡುಲೆನ್ ಒಂಜಿ ಮಲ್ಲ ಮರತ್ತ ಫಲಕೊಡು ಪತ್ತ್ ಮಲ್ಲ ಚಿಹ್ನೆಲೆನ್ ಅತ್ತಂಡ ಅಕ್ಷರೊಲೆನ್ ರೂಪಿಸೆರೆ ಜೋಡಾದ್ ದೀತೆರ್. ಒಂಜಿ ಸರ್ತಿ, ಫಲಕ ಅಯಿತ ಮೋನೆಡ್ ಫ್ಲಾಟ್ ಆದ್ ಬೂರುಂಡು. ಮರ ಕುಸಿತ್ಂಡ್, ಆಂಡ ಅಕ್ಷರೊಲೆನ ವ್ಯವಸ್ಥೆ ಒರಿದ್ಂಡ್. ಸೈನ್ ಬೋರ್ಡ್ ದ ಅಕ್ಷರೊಲು ಸುತ್ತಮುತ್ತದ ಗೋಡೆಲೆಡ್ ಬಳಕೆ ಆಯಿನ ಮಲ್ಲ ಇಟ್ಟಿಗೆಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಪ್ರತಿಯೊಂಜಿ ಚಿಹ್ನೆ ಸುಮಾರ್ 37 ಸೆಂ (15 ಇಂಚ್) ಎತ್ತರೊಡು ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಬೋರ್ಡ್ ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದ ಉಪ್ಪುಂಡು. ಈ ಶಾಸನ ಸಿಂಧು ಲಿಪಿಟ್ ಅತಿ ಉದ್ದೊದ ಶಾಸನೊಲೆಡ್ ಒಂಜಿ, ಒಂಜಿ ಚಿಹ್ನೆ ನಾಲ್ ಸರ್ತಿ ತೋಜುಂಡು, ಬೊಕ್ಕ ಅಯಿತ ಮಲ್ಲ ಗಾತ್ರ ಬೊಕ್ಕ ಸಾರ್ವಜನಿಕ ಸ್ವಭಾವೊ ಸಿಂಧು ಲಿಪಿ ಪೂರ್ತಿ ಸಾಕ್ಷರತೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಈ ಜಾಗೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ಲ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಒಂಜಿ ನಾಲ್ ಚಿಹ್ನೆದ ಶಾಸನೊನುಲಾ ತೂವೊಲಿ, ಉಂದು ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಇಂಚಿನ ಶಾಸನೊಲೆಡ್ ಸುರುತವು ಆದುಂಡು ಪನ್ಪೆರ್. [[ವರ್ಗ:Event:Heritage Across Borders]] cthik92w6xxx54nj756vpd3dbm5mxi6 366195 366184 2026-08-21T10:20:41Z Kavitha G. Kana 1040 /* ಉತ್ತರೊದ ದ್ವಾರ */ 366195 wikitext text/x-wiki [[File:DHOLAVIRA SITE (24).jpg|thumb|DHOLAVIRA SITE (24)]] ಧೋಲಾವಿರ ಪಂಡ ಪಶ್ಚಿಮ ಭಾರತೊದ [[ಗುಜರಾತ್]] ರಾಜ್ಯೊದ [[ಕಚ್]] ಜಿಲ್ಲೆದ ಭಾಚೌ ತಾಲೂಕುದ ಖಾದಿರ್ಬೆಟ್ ಡ್ ಉಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಜಾಗೆ, ಉಂದೆತ ಪುದರ್ ದಕ್ಷಿಣೊಡು 1 ಕಿಲೋಮೀಟರ್ (0.62 ಮೈಲ್) ದೂರೊಡು ಉಪ್ಪುನ ಇತ್ತೆದ ಗ್ರಾಮೊದ ಪುದರ್ ಡ್ ಉಂಡು. ಈ ಊರು ರಾಧನ್ಪುರ್ ಡ್ದ್ 165 ಕಿ.ಮೀ (103 ಮೈಲ್) ದೂರೊಡು ಉಂಡು. ಸ್ಥಳೀಯವಾದ್ ಕೋಟದ ಟಿಂಬಾ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಈ ಜಾಗೆಡ್ ಪ್ರಾಚೀನ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ನಗರೊದ ಅವಶೇಷೊಲು ಉಂಡು. ಭೂಕಂಪೊಲು ಧೋಲಾವಿರ ಪನ್ಪಿನ ಈ ಜಾಗೆಗ್ ಸುಮಾರ್ ಸರ್ತಿ ಅಯಿತ ಕೆಟ್ಟ ಪರಿಣಾಮೊನ್ ಬೀರ್ದ್ಂಡ್ .<ref>https://www.thehindu.com/society/ruins-on-the-tropic-of-cancer/article19284214.ece</ref> ==ಜಾಗೆ== ಧೋಲಾವಿರ ಪನ್ಪಿನ ಜಾಗೆ ಉಷ್ಣವಲಯೊಡು(Tropic of cancer) ಉಂಡು. ಉಂದು ಐನ್ ಮಲ್ಲ ಹರಪ್ಪನ್ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಬೊಕ್ಕ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆಗ್ ಸೇರ್ದಿನ ಭಾರತೊದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಪಿರಾಕ್ದ ತಾಣ. ಉಂದು ಕಚ್ ಮರುಭೂಮಿದ ವನ್ಯಜೀವಿ ಅಭಯಾರಣ್ಯೊಡು ಕಚ್ ಗ್ರೇಟ್ ರನ್ ಡ್ ಖಾದಿರ್ ಬೆಟ್ ದ್ವೀಪೊಡು ಉಂಡು. ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಚತುರ್ಭುಜಾಕೃತಿದ ನಗರ ಉತ್ತರೊಡು ಮನ್ಸಾರ್ ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹಾರ್ ಪನ್ಪಿನ ಸುದೆತ್ತ ನಡುಟು ಉಂಡು. ಕ್ರಿ.ಪೂ ೧೪೫೦; ಆಂಡಲಾ, ಇತ್ತೆದ ಸಂಶೋಧನೆ ಸುಮಾರ್ ಕ್ರಿ.ಪೂ.೩೫೦೦ಡ್ (ಹರಪ್ಪನ್ ಪೂರ್ವ) ಆಕ್ರಮಣದ ಸುರುತ ಬೊಕ್ಕ ಕ್ರಿ.ಪೂ ೧೮೦೦ (ಹರಪ್ಪನ್ ಕಾಲೊದ ಸುರುತ ಭಾಗ) ಮುಟ್ಟ ನಿರಂತರತೆನ್ ಸೂಚಿಸವುಂಡು ==ಅನ್ವೇಷಣೆ== ಈ ಜಾಗೆನ್ ಸುರುಕು ಧೋಲಾವಿರ ಗ್ರಾಮದ ನಿವಾಸಿ ಶಂಭೂದನ್ ಗಾಧವಿ 1960 ದ ಸುರುತ ಭಾಗೊಡು ಕಂಡು ಕನತ್ದ್, ಸರ್ಕಾರದ ಗಮನೊನು ಈ ಜಾಗೆಗ್ ಕನರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. ಈ ಜಾಗೆನ್ 1967 ಅತ್ತ್ಂಡ 1968ಡ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ದ ಜೆ.ಪಿ.ಜೋಶಿ "ಅಧಿಕೃತವಾದ್" ಬೊಳ್ಪುಗು ಕನತೆರ್, ಬೊಕ್ಕ ಉಂದು ಎಣ್ಮ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಐನ್ ನೇ ಮಲ್ಲ ಜಾಗೆ. ಉಂದು ೧೯೯೦ಡ್ದ್ ಎಎಸ್ಐಡ್ದ್ ಉತ್ಖನನೊಡು ಉಂಡು, ಉಂದು "ಧೋಲಾವಿರ ಸಿಂಧು ಕಣಿವೆದ ನಾಗರಿಕತೆದ ಅರ್ಥ ಮಲ್ಪುನಗ ನಿಜವಾದ್ ಲಾ ಪೊಸ ಆಯಾಮೊಲೆನ್ ಸೇರ್ಸಾದ್ಂಡ್" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ತೋಜಾದ್ಂಡ್. ಧೋಲಾವಿರಾನ್ ಕಚ್ ದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಮೆಗಾಲಿಥಿಕ್(ಶಿಲಾಯುಗ) ತಾಣೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಂಡುಂಡು, ಆಂಡ ಕಚ್ಚ್ ಡ್ ತೆರಿನ ಒಂಜೇ ಒಂಜಿ ಮೆಗಾಲಿಥಿಕ್ ತಾಣ ಧನೆತಿ ಗ್ರಾಮದ ಸಮೀಪೊಡು ಉಂಡು. ಇತ್ತೆ ಮುಟ್ಟ ತೋಜುದು ಬತ್ತಿನ ಬೇತೆ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ತಾಣೊಲು ಹರಪ್ಪಾ, [[ಮೊಹೆಂಜೊದಾರೊ,]] ಗಣೇರಿವಾಳ, ರಾಖಿಗಡಿ, ಕಾಳಿಬಂಗನ್, ರೂಪನಗರ ಬೊಕ್ಕ ಲೋಥಲ್. ಜುಲೈ ೨೭, ೨೦೨೧ಡ್ ಧೋಲಾವಿರ: ಒಂಜಿ ಹರಪ್ಪನ್ ನಗರ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಪುದರ್ ದೀಯೆರ್ ==ಉತ್ಖನನೊಲು== ಬಿಷ್ಟ್ ನ ನಿರ್ದೇಶನೊಡು ಎಎಸ್ಐ 1989ಡ್ ಉತ್ಖನನ ಸುರು ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ 1990 ಬೊಕ್ಕ 2005 ನಡುಟು 13 ಕ್ಷೇತ್ರ ಉತ್ಖನನೊಲು ನಡತ್ಂಡ್. ಉತ್ಖನನ ನಗರ ಯೋಜನೆ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಬೊಲ್ಪುಗು ಕನೆತೆರ್, ಬೊಕ್ಕ ಪ್ರಾಣಿಲೆನ ಮೂಳೆಲು, ಚಿನ್ನ, ಬೊಳ್ಳಿ, ಟೆರ್ನಾಕೊಟರ್, ಬೊಕ್ಕ ಕಂಚಿದಂಚಿನ ಮಸ್ತ್ ಸಂಖ್ಯೆಡ್ ಪ್ರಾಚೀನ ವಸ್ತುಲೆನ್ ಕಂಡು ಕನತ್ಂಡ್. ದಕ್ಷಿಣ ಗುಜರಾತ್, ಸಿಂಧ್ ಬೊಕ್ಕ ಪಂಜಾಬ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿ ಏಷ್ಯಾದ ವಸತಿ ಸ್ಥಾನೊಲೆನ ನಡುಟು ಧೋಲಾವಿರ ಒಂಜಿ ಪ್ರಮುಖ ವ್ಯಾಪಾರ ಕೇಂದ್ರ ಆದಿತ್ತ್ಂಡ್ ಪಂದ್ ಪುರಾತತ್ತ್ವಜ್ಞೆರ್ ನಂಬುವೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== [[ಲೋಥಲ್]] ಬಂದರ್-ನಗರೊಡ್ದ್ ಪಿರಾಕ್ ದವು ಪನ್ಪಿನ ಅಂದಾಜಿಡ್, ಧೋಲಾವಿರಾ ನಗರೊಗು ಆಯತಾಕಾರೊದ ಆಕಾರೊ ಬೊಕ್ಕ ಸಂಘಟನೆ ಉಂಡು, ಬೊಕ್ಕ ಉಂದು 22 ಹೆಕ್ಟೇರ್ (54 ಎಕ್ರೆ) ಡ್ ವ್ಯಾಪಿಸಾಯಿನವು. ಈ ಪ್ರದೇಶೊದ ಉದ್ದ 771.1 ಮೀ (2,530 ಅಡಿ), ಬೊಕ್ಕ ಅಗಲ 616.85 ಮೀ (2,023.8 ಅಡಿ) ಉಂಡು. ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊದಾರೊದಲೆಕ ಅತ್ತ್, ಈ ನಗರೊನು ಮೂಜಿ ವಿಭಾಗೊಲೆನ್ ಸೇರ್ದಂಚಿನ ಒಂಜಿ ದುಂಬೇ ಇತ್ತಿನ ಜ್ಯಾಮಿತೀಯ ಯೋಜನೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ - ಮಿತ್ತ ಕೋಟೆ,ನಡುತ್ತ ಊರು, ಬೊಕ್ಕ ತಿರ್ತದ ಊರು. ಅಕ್ರೊಪೊಲಿಸ್ ಬೊಕ್ಕ ನಡುತ್ತ ಊರು ಅಕ್ಲೆನ ಸ್ವಂತ ರಕ್ಷಣಾ ಕೆಲಸ, ದ್ವಾರೊಲು, ನಿರ್ಮಾಣ ಆಯಿನ ಪ್ರದೇಶೊಲು,ಸಾದಿ ವ್ಯವಸ್ಥೆ,ಗುವೆಲ್ ಬೊಕ್ಕ ಮಲ್ಲ ತೆರೆದಿಪ್ಪುನ ಜಾಗೊಲೆನ್ ಸಜ್ಜು ಮಲ್ತೆರ್.ಉಂದೆಟ್ ನೈಋತ್ಯ ವಲಯೊದ ಪ್ರಮುಖ ಭಾಗೊನು ಸ್ವಾಧೀನ ಮಲ್ಪುಂಡು. ಎತ್ತರದ "ಕೋಟೆ" ನಿಲೆಲೆನ್ ಡಬಲ್ ರಾಂಪಾರ್ಟ್ ಲೆಡ್ ರಕ್ಷಣೆ ಮಲ್ಪುಂಡು. ಉಂದೆತ ಬರಿಟ್ 'ಬೈಲಿ' ಪನ್ಪಿನ ಜಾಗೆ ಉಂಡು ಅಲ್ಪ ಪ್ರಮುಖ ಅಧಿಕಾರಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಸಾಮಾನ್ಯ ಕೋಟೆಲೆನ ಉಳಯಿ ಉಪ್ಪುನ ನಗರ ಪ್ರದೇಸೊ 48 ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಉಂಡು. ಕೋಟೆದ ವಸಾಹತುಗು ಅವಿಭಾಜ್ಯವಾದ್ ಉಪ್ಪುನ ವಿಸ್ತಾರವಾದ್ ರಚನೆ-ಬೇರಿಂಗ್ ಪ್ರದೇಶೊಲು ಉಂಡು. ಗೋಡೆಲೆನ ಮಿತ್ತ್, ಬೇತೆ ಒಂಜಿ ವಸಾಹತು ತೋಜಿದ್ಬತ್ತ್ಂಡ್. ನಗರೊದ ಅತ್ಯಂತ ಗಮನಾರ್ಹ ಲಕ್ಷಣ ಪಂಡ ಅಯಿತ ಮಾತಾ ಕಟ್ಟಡೊಲು, ಕನಿಷ್ಠ ಅಯಿತ ಇತ್ತೆದ ಸಂರಕ್ಷಣೆದ ಸ್ಥಿತಿಡ್, ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್, ಆಂಡ ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊ-ದಾರೊ ಸೇರ್ದ್ ಬೇತೆ ಹರಪ್ಪನ್ ತಾಣೊಲು ಮಾತಲಾ ಇಟ್ಟಿಗೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಧೋಲಾವಿರಾ ರಡ್ಡ್ ತುಫಾನ್ ನೀರ್ದ ಕಾಲುವೆಲೆಡ್ದ್ ಸುತ್ತುವರಿದ್ ಉಂಡು; ಉತ್ತರೊಡು ಮನ್ಸರ್, ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹರ್. ಊರುದ ಚೌಕೊಡು, ನೆಲತ ಮಿತ್ತ್ ಎತ್ತರೊಡು ಒಂಜಿ ಜಾಗೆ ಉಂಡು, ಅವೆನ್ "ಕೋಟೆ" ಪನ್ಪೆರ್.<ref>https://web.archive.org/web/20170812062930/https://archaeologynewsnetwork.blogspot.com/2010/06/rise-and-fall-of-harappan-city.html</ref> ==ಜಲಾಶಯೊಲು== ಎಎಸ್ಐ ದ ಜಂಟಿ ಮಹಾನಿರ್ದೇಶಕೆರಾದ್ ನಿವೃತ್ತರಾಯಿನ ಬಿಷ್ತ್ ಪನ್ಪೆರ್, "ನೀರ್ ದ ಸಂರಕ್ಷಣೆ, ಕೊಯ್ಲು ಬೊಕ್ಕ ಸಂಗ್ರಹೊಗು ಅಭಿವೃದ್ಧಿ ಮಲ್ತಿನ ಧೋಲಾವಿರದ ಹರಪ್ಪನೆರೆನ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಸ್ಥೆ ಅಕ್ಲೆನ ಸುಧಾರಿತ ಹೈಡ್ರಾಲಿಕ್ ಎಂಜಿನಿಯರಿಂಗ್ ದ ಬಗ್ಗೆ ಪಾತೆರುಂಡು, ಕ್ರಿ.ಪೂ. ಮೂಜಿನೆ ಸಹಸ್ರಮಾನೊಡು ತಂತ್ರಜ್ಞಾನದ ಸ್ಥಿತಿನ್ ತೂಂಡ ಡೊಲಾವಿರಾದ ಸೋಫ್ಯಾಸ್ಟಿಕ್ ನೀರ್ ದ ಅನನ್ಯ ಲಕ್ಷಣೊಲು ಒಂಜಿ." ಚಾನೆಲ್ ಬೊಕ್ಕ ಜಲಾಶಯೊಲೆನ ಸಂರಕ್ಷಣೆದ ವ್ಯವಸ್ಥೆ, ಜಗತ್ತ್ಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಸುರುತ ವ್ಯವಸ್ಥೆ, ಉಂದು ಪೂರ್ತಿ ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ನಗರೊಡು ಮಲ್ಲ ಮಲ್ಲ ಜಲಾಶಯೊಲು ಇತ್ತ್ಂಡ್, ಅವುಲು ಮೂಜಿ ಜಲಾಶಯೊಲು ಬಹಿರಂಗ ಆತ್ಂಡ್. ಅವು ರಡ್ಡ್ ಗಿಟ್ಟದ ಕೆರೆಕುಲೆಡ್ದ್ ಬೇತೆ ಬೇತೆ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಬರ್ಸೊಡ್ದ್ ಬತ್ತಿನ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಆಪುಂಡು. ಉಂದು ಸ್ಪಷ್ಟವಾದ್ ಕಚ್ ದ ಮರುಭೂಮಿದ ಹವಾಮಾನ ಬೊಕ್ಕ ಪರಿಸ್ಥಿತಿಲೆಗ್ ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ಬತ್ತ್ಂಡ್, ಅಲ್ಪ ಕೆಲವು ವರ್ಷೊಲು ಬರ್ಸ ಬರ್ಪುಜಿ. ಈ ಜಾಗೆದ ಸಮೀಪೊಡು ಉತ್ತರ-ದಕ್ಷಿಣ ದಿಕ್ಕ್ ಡ್ ಪೋಪಿನ ಒಂಜಿ ಋತುಕಾಲಿಕ ನೀರ್ ನ್ ನೀರ್ ಸಂಗ್ರಹ ಮಲ್ಪರೆ ಕೆಲವು ಕಡೆಟ್ ಅಣೆಕಟ್ಟ್ ಮಲ್ತೆರ್. ೧೯೯೮ಡ್, ಈ ಜಾಗೆಡ್ ಬೇತೆ ಒಂಜಿ ಜಲಾಶಯೊನು ಕಂಡು ಕನತ್ ದೆರ್.<ref>https://www.cntraveller.in/magazine-story/india-via-gujarat/</ref> ಧೋಲಾವಿರಾದ ನಿವಾಸಿಲು ಮೂಜಿನೆ ಹಂತೊಡು ಬೇತೆ ಬೇತೆ ಗಾತ್ರೊದ ಪದಿನಾಜಿ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ಜಲಾಶಯೊಲೆನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಅವುಲು ಕೆಲವು ಮಲ್ಲ ವಸಾಹತುದ ತಿರ್ತ್ ನೆಲತ ಇಳಿಜಾರು ಪ್ರದೇಸೊದ ಲಾಭ ದೆತೊನುವೆರ್ ಅವುಲು ಗುವೆಲ್ದಂಚಿನ ರಚನೆ ಮಲ್ಪುವೆರ್.. ಬೇತೆ ಜಲಾಶಯೊಲೆನ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಕೆಲವು ಕಲ್ಲ್ಡ್ ಕಟ್ಟ್ದೆರ್ . ಇತ್ತೆದ ಕೆಲಸೊಡು ರಡ್ಡ್ ಮಲ್ಲ ಜಲಾಶಯೊಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್, ಒಂಜಿ ಕೋಟೆದ ಪೂರ್ವೊಡ್ ಬೊಕ್ಕೊಂಜಿ ಅಯಿತ ತೆಂಕ್ ದಿಕ್ಕ್ ಗ್ ಅನೆಕ್ಸ್ ದ ಸಮೀಪೊಡು. ಜಲಾಶಯೊಲು ಕಲ್ಲ್ ಡ್ದ್ ಲಂಬವಾದ್ ಕಡ್ತ್ ದ್ ಉಪ್ಪುಂಡು, ಬೊಕ್ಕ ಅವು ಸುಮಾರ್ 7 ಮೀ (23 ಅಡಿ) ಆಳೊಡು ಬೊಕ್ಕ 79 ಮೀ (259 ಅಡಿ) ಉದ್ದೊಡು ಉಪ್ಪುಂಡು. ಅವು ನಗರೊನು ಸುತ್ತುವರಿದ್ ಉಪ್ಪುಂಡು, ಆಂಡ ಕೋಟೆ ಬೊಕ್ಕ ಮೀಪುನ ಸ್ನಾನಘಟ್ಟಡ್ ಕೇಂದ್ರೊಡು ಎತ್ತರದ ನೆಲಟ್ ಉಪ್ಪುಂಡು. ಒಂಜಿ ಮಲ್ಲ ಗೂವೆಲ್ ಲಾ ಉಂಡು, ಉಂದೆಟ್ ಒಂಜಿ ಕಲ್ಲ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ಟ್ರಫ್ ನ್ ಒಂಜಿ ಡ್ರೈನ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು, ಉಂದು ಒಂಜಿ ಟ್ಯಾಂಕ್ ಗ್ ನೀರ್ ನ್ ಕನಪುಂಡು.ಮೀಪುನ ಜಾಗೆಗ್ ಬರ್ಪಿನವುಲು ಮೆಟ್ಟಿಲುಲು ಉಪ್ಪುಂಡು.<ref>https://whc.unesco.org/en/tentativelists/5892/</ref> ಅಕ್ಟೋಬರ್ ೨೦೧೪ಡ್, ಒಂಜಿ ಆಯತಾಕಾರದ ಮುಟ್ಟ್ ಉಪ್ಪುನ ಗುವೆಲ್ಡ್ ಉತ್ಖನನ ಸುರು ಆಂಡ್, ಉಂದು ೭೩.೪ ಮೀ (೨೪೧ ಅಡಿ) ಉದ್ದ, ೨೯.೩ ಮೀ (೯೬ ಅಡಿ) ಅಗಲ, ಬೊಕ್ಕ ೧೦ ಮೀ (೩೩ ಅಡಿ) ಆಳೊಡು ಇತ್ತ್ಂಡ್, ಉಂದು ಮೊಹೆಂಜೇದಾರೊದ ಮಲ್ಲ ಸ್ನಾನಘಟ್ಟೊಡ್ದ್ ಮೂಜಿ ಪಾಲ್ ಮಲ್ಲವು. ==ಮುದ್ರೆ ಮಲ್ಪುನ== ಧೋಲಾವಿರಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಕೆಲವು ಮುದ್ರೆಲು, ಸ್ಟೇಜ್ III ಗ್ ಸೇರ್ದಿನವು, ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಮಾತ್ರ ಮೂಲು ತೂವೋಲಿ ಅತ್ತಂದೆ, ಒವ್ವೆ ರೀತಿದ ಲಿಪಿ ಇಜ್ಜಿ. ಈ ರೀತಿದ ಮುದ್ರೆಲು ಸಿಂಧು ಮುದ್ರೆ ತಯಾರಿಕೆದ ಪಿರಾಕ್ ದ ಸಂಪ್ರದಾಯೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ==ಬೇತೆ ರಚನೆಲು ಬೊಕ್ಕ ವಸ್ತುಲು== ==ಪೂರ್ವೊದ ದ್ವಾರ== ಆ ಜಾಗೆಡ್ ಒಂಜಿ ಮಲ್ಲ ವೃತ್ತಾಕಾರದ ರಚನೆ ಒಂಜಿ ಸಮಾಧಿ ಅತ್ತಂಡ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ನಂಬುನವು ತಿಕ್ಕ್ಂಡ್, ಆಂಡಲಾ ಅಯಿಟ್ ಅಸ್ಥಿಪಂಜರೊಲು ಅತ್ತಂಡ ಬೇತೆ ಮಾನವ ಅವಶೇಷೊಲು ಇಜ್ಜಿ. ಈ ರಚನೆಡ್ ಒಂಜಿ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ಆಕಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಪತ್ತ್ ರೇಡಿಯಲ್ ಮಣ್ಣ್-ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು ಉಂಡು. ಫಾಲಸ್ ಎರೆಕ್ಟಸ್ ಆಂಡ ತರೆ ಬೊಕ್ಕ ಕಾರ್ ಪಾದೊದ ತಿರ್ತ್ ಕತ್ತರಿಸಾಯಿನ ಒಂಜಿ ಪುರುಷನ ಮೃದುವಾಯಿನ ಮರಳುಕಲ್ಲುದ ಶಿಲ್ಪೊನು ಪೂರ್ವ ದ್ವಾರೊದ ಮಾರ್ಗೊಡು ತೂಯೆರ್. ಮಸ್ತ್ ಶವಸಂಸ್ಕಾರದ ರಚನೆಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ (ಒಂಜಿ ಬುಕ್ಕೊ ಮಾತಾ ಅಸ್ಥಿಪಂಜರೊಲು ಇಜ್ಜಿ), ಅಂಚನೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಟೆರಾಕೋಟಾ ಮುದ್ರೆಲು, ಬಳೆ, ಉಂಗುರೊಲು, ಮಣಿ, ಬೊಕ್ಕ ಕೆತ್ತನೆ. ಡಾನ್ಸಿಂಗ್ ಗರ್ಲ್ ಪಂಡ ಹರಪ್ಪನ್ ನಾಗರಿಕತೆದ ಅತ್ಯಂತ ಪ್ರತಿಷ್ಠಿತ ಕಲಾಕೃತಿಲೆಡ್ ಒಂಜಿ, ಮೊಹೆಂಜೊದಾರೊಡು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಮಾರ್ ಕ್ರಿ.ಪೂ. 2500 ಡ್ದ್ ದುಂಬುದ ಈ ಕಂಚಿದ ಪ್ರತಿಮೆ ಸುಮಾರ್ 10.5 ಸೆಂಟಿಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು ಬೊಕ್ಕ ಒರಿ ಜವನೆರೆನ ನರ್ತಕಿನ ಕ್ರಿಯಾತ್ಮಕ ಶಕ್ತಿ ಬೊಕ್ಕ ಅನುಗ್ರಹೊನು ಸ್ಪಷ್ಟವಾದ್ ಸೆರೆಪತ್ತ್ದ್ಂಡ್. ==ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು== [[File:Cicular houses Dholavira.jpg|thumb|Circular houses of Dholavira - an ancient Harappan City]] ಧೋಲಾವಿರಾಡ್ ಏಳ್ ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲೆನ್ ತೂಯೆರ್, ಅವುಲು ರಡ್ಡ್ ನ್ ವಿವರವಾದ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಅವು ಮಲ್ಲ ಕಲ್ಲ್ ಕತ್ತರಿಸಾಯಿನ ಕೋಣೆಲೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಯೋಜನೆನ್ ಪಡೆಯಿನವು, ಅವು ಮಲ್ಲ ಅರ್ಧಗೋಳಾಕಾರದ ಎತ್ತರದ ಮಣ್ಣ್ ದ ಇಟ್ಟಿಗೆದ ನಿರ್ಮಾಣೊಲು. ಉತ್ಖನನ ಮಲ್ತಿನ ಒಂಜಿ ರಚನೆನ್ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೂಪೊಡು ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕೊಂಜಿಲಾ ಅದೇ ರೀತಿಡ್ ಡಿಸೈನ್ ಮಲ್ತೆರ್, ಆಂಡ ಸ್ಪೋಕ್ಸ್ ಇಜ್ಜಂದಿನ ಚಕ್ರೊದಲೆಕ. ಅವುಲು ಕುಂಬಾರಿಕೆದ ಸಮಾಧಿದ ವಸ್ತುಲು ಇತ್ತ್ಂಡಲಾ, ಒಂಜಿ ಸಮಾಧಿನ್ ಬುಡ್ದು ಬೇತೆ ಒವ್ವೆ ಅಸ್ಥಿಪಂಜರೊಲು ತೋಜಿಜಿ, ಅಲ್ಪ ಒಂಜಿ ಅಸ್ಥಿಪಂಜರ ಬೊಕ್ಕ ಒಂಜಿ ತಾಮ್ರದ ಕನ್ನಡಿ ಉಂಡು. ಒಂಜಿ ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಡ್ ಒಂಜಿ ತಾಮ್ರದ ತಂತಿಗ್ ಕೊಕ್ಕೆಲೆನ್ ಕಟ್ಟ್ ದ್ ದೀತಿನ ಸ್ಟೀಟೈಟ್ ಮಣಿಲೆನ ಕಂಕಣ, ಒಂಜಿ ಬಂಗಾರ್ದ ಬಳೆ, ಬಂಗಾರ್ ಬೊಕ್ಕ ಬೇತೆ ಮಣಿಕುಲುಲಾ ಒಂಜಿ ಅರ್ಧಗೋಳದ ರಚನೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಉತ್ಖನನೊನು ನಡಪಾಯಿನ ಭಾರತದ ಪುರಾತತ್ವ ಸಮೀಕ್ಷೆ, ಈ "ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಲು ಪಿರಾಕ್ದ ಬೌದ್ಧ ಸ್ತೂಪೊಲೆನ್ ನೆನಪು ಮಲ್ಪುಂಡು" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ಪನ್ಪುಂಡು. ಬೊಕ್ಕ "ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ಬೊಕ್ಕ ಅನ್ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೀತಿದ ವಿನ್ಯಾಸ ಶತಪಥ ಬ್ರಾಹ್ಮಣ ಬೊಕ್ಕ ಶುಲ್ಬ ಸೂತ್ರೊಡು ನಮೂದಿಸಾಯಿನ ಸರರತ-ಚಕ್ರ-ಸಿಟಿ ಬೊಕ್ಕ ಸಪ್ರಧಿ-ರತ-ಚಕ್ರ-ಸಿಟಿನ್ ನೆಂಪು ಮಲ್ಪುಂಡು" ==ಉತ್ತರೊದ ದ್ವಾರ== [[File:Dholavira-1.jpg|thumb|ಧೋಲಾವಿರ-1]] ಸಿಂಧು ಲಿಪಿತ ಮಿತ್ತ್ ಕಪ್ಪು ಬಣ್ಣದ ಕುಂಬಾರಿಕೆ, ಚೌಕ ಮುದ್ರೆದ ಮುದ್ರೆಲು, ಸಿಂಧು ಲಿಪಿ ಇಜ್ಜಂದಿನ ಮುದ್ರೆಲು, ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದೊದ ಒಂಜಿ ಮಲ್ಲ ಸೈನ್ ಬೋರ್ಡ್, ಸಿಂಧು ಲಿಪಿತ ಪತ್ತ್ ಅಕ್ಷರೊಲೆನ್ ಸೇರ್ದುಂಡು. ಕಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಒಂಜಿ ಕಳಪೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಕುಲ್ಲುನ ಪುರುಷ ರೂಪೊನುಲಾ ತೂಯೆರ್, ಉಂದು ಹರಪ್ಪಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಉನ್ನತ ಗುಣಮಟ್ಟದ ರಡ್ಡ್ ಕಲ್ಲ್ ದ ಶಿಲ್ಪೊಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಮುಳ್ಳುದ ನೆಲತ ಮಲ್ಲ ಕಪ್ಪು ಜಾರ್ ಲೆನ್ ಲಾ ಈ ಜಾಗೆಡ್ ತೂಯೆರ್. ಒಂಜಿ ಮಲ್ಲ ಕಂಚಿದ ಸುತ್ತಿಗೆ, ಒಂಜಿ ಮಲ್ಲ ಉಳಿ, ಒಂಜಿ ಕಂಚಿನ ಕೈಟ್ ಪತ್ತೊಂದಿಪ್ಪುನ ಕನ್ನಡಿ, ಒಂಜಿ ಬಂಗಾರ್ದ ತಂತಿ, ಬಂಗಾರ್ದ ಕೆಬಿ ಓಲೆ, ತಾಮ್ರದ ಸೆಲ್ಟ್ಸ್ ಬೊಕ್ಕ ಚಿಪ್ಪುದ ಬಳೆ, ಕಲ್ಲ್ ದ ಫಾಲಸ್ ಲೆಕೊದ ಚಿಹ್ನೆಲು, ಸಿಂಧು ಶಾಸನ ಬೊಕ್ಕ ಚಿಹ್ನೆಲೆನ್ ದೀದ್ ಚೌಕ ಮುದ್ರೆಲು, ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಮುದ್ರೆ, ಕುಂಟುದ ಪ್ರಾಣಿಲು, ಪಾಟರ್ ಪೇಂಟೆಡ್ ಪಾಟರ್ಡ್, ಗೊಬ್ಬುಲು, ಡಿಶ್-ಆನ್-ಸ್ಟ್ಯಾಂಡ್, ರಂದ್ರೊಡುಪ್ಪುನ ಜಾರ್ ಲು, ಎಡ್ಡೆ ಆಕಾರೊಡು ಉಪ್ಪುನ ಟೆರಾಕೊಟಾ ಟಂಬ್ಲರ್ ಲು,ಕಡೆಪ್ಪುನ ಕಲ್ಲ್, ಗಾರೆಲು ಇಂಚಿನವುಲಾ ಈ ಜಾಗೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಬೇತೆ ಬೇತೆ ಅಳತೆದ ಕಲ್ಲ್ ದ ತೂಕೊಲೆನ್ ಲಾ ತೂಯೆರ್. ==ಕರಾವಳಿ ಮಾರ್ಗ== ಲೋಥಲ್ ಬೊಕ್ಕ ಧೋಲಾವಿರಾನ್ ಮಕ್ರಾನ್ ಕರಾವಳಿಡ್ ಸುತ್ಕಗನ್ ದೋರ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುನ ಒಂಜಿ ಕರಾವಳಿ ಮಾರ್ಗ ಉಂಡು. ==ಬಾಸೆ ಬೊಕ್ಕ ಲಿಪಿ == ಹರಪ್ಪನೆರ್ ಒಂಜಿ ಅಜ್ಞಾತ ಬಾಸೆನ್ ಪಾತೆರೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಅಕ್ಲೆನ ಲಿಪಿನ್ ಇತ್ತೆಲಾ ಅರ್ಥ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಉಂದೆಟ್ ಸುಮಾರ್ 400 ಮೂಲ ಚಿಹ್ನೆಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ನಂಬಿಕೆ ಉಂಡು, ಉಂದೆಟ್ ಮಸ್ತ್ ಬದಲಾವಣೆಲು ಉಂಡು. ಚಿಹ್ನೆಲು ಪದೊಕುಲೆಗ್ ಬೊಕ್ಕ ಉಚ್ಚಾರೊಲೆಗ್ ನಿಲೆಯಾದಿತ್ತ್ ದ್ ಉಪ್ಪೊಲಿ. ಬರವುದ ದಿಕ್ಕು ಸಾಮಾನ್ಯವಾದ್ ಬಲತ್ತ ಕಡೆಟ್ದ್ ಎಡತ್ತ್ ಗ್ ಇತ್ತ್ಂಡ್. ಹೆಚ್ಚಿನ ಶಾಸನೊಲು ಮುದ್ರೆಲೆಡ್ (ಬಹುತೇಕ ಕಲ್ಲ್ ಡ್ದ್ ಮಲ್ಪುನವು) ಬೊಕ್ಕ ಮುದ್ರೆಲೆಡ್ (ಮಣ್ಣ್ ದ ತುಂಡುಲೆಡ್ ಮುದ್ರೆನ್ ಒತ್ತುದು ಅಯಿತ ಗುರುತುನ್ ಬುಡ್ಪೆರ್) ಉಪ್ಪುಂಡು. ಕೆಲವು ಶಾಸನೊಲು ತಾಮ್ರದ ಫಲಕೊಲೆಡ್, ಕಂಚಿದ ಉಪಕರಣೊಲೆಡ್, ಬೊಕ್ಕ ಟೆರಾಕೊಟಾ, ಕಲ್ಲ್ ಬೊಕ್ಕ ಫೇಯೆನ್ಸ್ ಡ್ ಮಲ್ತಿನ ಎಲ್ಯ ವಸ್ತುಲೆಡ್ಲಾ ತೋಜುಂಡು. ಈ ಮುದ್ರೆಲೆನ್ ವ್ಯಾಪಾರೊಡು ಬೊಕ್ಕ ಅಧಿಕೃತ ಆಡಳಿತ ಕೆಲಸೊಗುಲಾ ಬಳಕೆ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಮೊಹೆಂಜೊದಾರೊ ಬೊಕ್ಕ ಬೇತೆ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ಜಾಗೆಲೆಡ್ ಮಸ್ತ್ ಕೆತ್ತನೆದ ಸಾಮಗ್ರಿಲೆನ್ ತೂಯೆರ್.<ref>https://web.archive.org/web/20110903114242/http://asi.nic.in/asi_exca_2007_dholavira.asp</ref> ==ಸೈನ್ ಬೋರ್ಡ್== [[File:The 'Ten Indus Scripts' discovered near the northern gateway of the Dholavira citadel.jpg|thumb|Ten Indus characters from the northern gate of Dholavira, dubbed the Dholavira Signboard, one of the longest known sequences of Indus characters]] ಧೋಲಾವಿರೊಡು ಮಸ್ತ್ ಮಹತ್ವದ ಅನ್ವೇಷಣೆಲು ನಗರೊದ ಉತ್ತರ ದ್ವಾರೊದ ಒಂಜಿ ಸೈಡ್ ರೂಮ್ ಡ್ ನಡತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಧೋಲಾವಿರ ಸೈನ್ಬೋರ್ಡ್ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ಹರಪ್ಪೆರ್ ಖನಿಜ ಜಿಪ್ಸಮ್ ದ ತುಂಡುಲೆನ್ ಒಂಜಿ ಮಲ್ಲ ಮರತ್ತ ಫಲಕೊಡು ಪತ್ತ್ ಮಲ್ಲ ಚಿಹ್ನೆಲೆನ್ ಅತ್ತಂಡ ಅಕ್ಷರೊಲೆನ್ ರೂಪಿಸೆರೆ ಜೋಡಾದ್ ದೀತೆರ್. ಒಂಜಿ ಸರ್ತಿ, ಫಲಕ ಅಯಿತ ಮೋನೆಡ್ ಫ್ಲಾಟ್ ಆದ್ ಬೂರುಂಡು. ಮರ ಕುಸಿತ್ಂಡ್, ಆಂಡ ಅಕ್ಷರೊಲೆನ ವ್ಯವಸ್ಥೆ ಒರಿದ್ಂಡ್. ಸೈನ್ ಬೋರ್ಡ್ ದ ಅಕ್ಷರೊಲು ಸುತ್ತಮುತ್ತದ ಗೋಡೆಲೆಡ್ ಬಳಕೆ ಆಯಿನ ಮಲ್ಲ ಇಟ್ಟಿಗೆಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಪ್ರತಿಯೊಂಜಿ ಚಿಹ್ನೆ ಸುಮಾರ್ 37 ಸೆಂ (15 ಇಂಚ್) ಎತ್ತರೊಡು ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಬೋರ್ಡ್ ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದ ಉಪ್ಪುಂಡು. ಈ ಶಾಸನ ಸಿಂಧು ಲಿಪಿಟ್ ಅತಿ ಉದ್ದೊದ ಶಾಸನೊಲೆಡ್ ಒಂಜಿ, ಒಂಜಿ ಚಿಹ್ನೆ ನಾಲ್ ಸರ್ತಿ ತೋಜುಂಡು, ಬೊಕ್ಕ ಅಯಿತ ಮಲ್ಲ ಗಾತ್ರ ಬೊಕ್ಕ ಸಾರ್ವಜನಿಕ ಸ್ವಭಾವೊ ಸಿಂಧು ಲಿಪಿ ಪೂರ್ತಿ ಸಾಕ್ಷರತೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಈ ಜಾಗೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ಲ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಒಂಜಿ ನಾಲ್ ಚಿಹ್ನೆದ ಶಾಸನೊನುಲಾ ತೂವೊಲಿ, ಉಂದು ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಇಂಚಿನ ಶಾಸನೊಲೆಡ್ ಸುರುತವು ಆದುಂಡು ಪನ್ಪೆರ್. [[ವರ್ಗ:Event:Heritage Across Borders]] 5eshld7dtsr4m2eps5bb8caa4bzc7lo 366198 366195 2026-08-21T10:22:45Z Kavitha G. Kana 1040 /* ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು */ 366198 wikitext text/x-wiki [[File:DHOLAVIRA SITE (24).jpg|thumb|DHOLAVIRA SITE (24)]] ಧೋಲಾವಿರ ಪಂಡ ಪಶ್ಚಿಮ ಭಾರತೊದ [[ಗುಜರಾತ್]] ರಾಜ್ಯೊದ [[ಕಚ್]] ಜಿಲ್ಲೆದ ಭಾಚೌ ತಾಲೂಕುದ ಖಾದಿರ್ಬೆಟ್ ಡ್ ಉಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಜಾಗೆ, ಉಂದೆತ ಪುದರ್ ದಕ್ಷಿಣೊಡು 1 ಕಿಲೋಮೀಟರ್ (0.62 ಮೈಲ್) ದೂರೊಡು ಉಪ್ಪುನ ಇತ್ತೆದ ಗ್ರಾಮೊದ ಪುದರ್ ಡ್ ಉಂಡು. ಈ ಊರು ರಾಧನ್ಪುರ್ ಡ್ದ್ 165 ಕಿ.ಮೀ (103 ಮೈಲ್) ದೂರೊಡು ಉಂಡು. ಸ್ಥಳೀಯವಾದ್ ಕೋಟದ ಟಿಂಬಾ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಈ ಜಾಗೆಡ್ ಪ್ರಾಚೀನ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ನಗರೊದ ಅವಶೇಷೊಲು ಉಂಡು. ಭೂಕಂಪೊಲು ಧೋಲಾವಿರ ಪನ್ಪಿನ ಈ ಜಾಗೆಗ್ ಸುಮಾರ್ ಸರ್ತಿ ಅಯಿತ ಕೆಟ್ಟ ಪರಿಣಾಮೊನ್ ಬೀರ್ದ್ಂಡ್ .<ref>https://www.thehindu.com/society/ruins-on-the-tropic-of-cancer/article19284214.ece</ref> ==ಜಾಗೆ== ಧೋಲಾವಿರ ಪನ್ಪಿನ ಜಾಗೆ ಉಷ್ಣವಲಯೊಡು(Tropic of cancer) ಉಂಡು. ಉಂದು ಐನ್ ಮಲ್ಲ ಹರಪ್ಪನ್ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಬೊಕ್ಕ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆಗ್ ಸೇರ್ದಿನ ಭಾರತೊದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಪಿರಾಕ್ದ ತಾಣ. ಉಂದು ಕಚ್ ಮರುಭೂಮಿದ ವನ್ಯಜೀವಿ ಅಭಯಾರಣ್ಯೊಡು ಕಚ್ ಗ್ರೇಟ್ ರನ್ ಡ್ ಖಾದಿರ್ ಬೆಟ್ ದ್ವೀಪೊಡು ಉಂಡು. ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಚತುರ್ಭುಜಾಕೃತಿದ ನಗರ ಉತ್ತರೊಡು ಮನ್ಸಾರ್ ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹಾರ್ ಪನ್ಪಿನ ಸುದೆತ್ತ ನಡುಟು ಉಂಡು. ಕ್ರಿ.ಪೂ ೧೪೫೦; ಆಂಡಲಾ, ಇತ್ತೆದ ಸಂಶೋಧನೆ ಸುಮಾರ್ ಕ್ರಿ.ಪೂ.೩೫೦೦ಡ್ (ಹರಪ್ಪನ್ ಪೂರ್ವ) ಆಕ್ರಮಣದ ಸುರುತ ಬೊಕ್ಕ ಕ್ರಿ.ಪೂ ೧೮೦೦ (ಹರಪ್ಪನ್ ಕಾಲೊದ ಸುರುತ ಭಾಗ) ಮುಟ್ಟ ನಿರಂತರತೆನ್ ಸೂಚಿಸವುಂಡು ==ಅನ್ವೇಷಣೆ== ಈ ಜಾಗೆನ್ ಸುರುಕು ಧೋಲಾವಿರ ಗ್ರಾಮದ ನಿವಾಸಿ ಶಂಭೂದನ್ ಗಾಧವಿ 1960 ದ ಸುರುತ ಭಾಗೊಡು ಕಂಡು ಕನತ್ದ್, ಸರ್ಕಾರದ ಗಮನೊನು ಈ ಜಾಗೆಗ್ ಕನರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. ಈ ಜಾಗೆನ್ 1967 ಅತ್ತ್ಂಡ 1968ಡ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ದ ಜೆ.ಪಿ.ಜೋಶಿ "ಅಧಿಕೃತವಾದ್" ಬೊಳ್ಪುಗು ಕನತೆರ್, ಬೊಕ್ಕ ಉಂದು ಎಣ್ಮ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಐನ್ ನೇ ಮಲ್ಲ ಜಾಗೆ. ಉಂದು ೧೯೯೦ಡ್ದ್ ಎಎಸ್ಐಡ್ದ್ ಉತ್ಖನನೊಡು ಉಂಡು, ಉಂದು "ಧೋಲಾವಿರ ಸಿಂಧು ಕಣಿವೆದ ನಾಗರಿಕತೆದ ಅರ್ಥ ಮಲ್ಪುನಗ ನಿಜವಾದ್ ಲಾ ಪೊಸ ಆಯಾಮೊಲೆನ್ ಸೇರ್ಸಾದ್ಂಡ್" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ತೋಜಾದ್ಂಡ್. ಧೋಲಾವಿರಾನ್ ಕಚ್ ದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಮೆಗಾಲಿಥಿಕ್(ಶಿಲಾಯುಗ) ತಾಣೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಂಡುಂಡು, ಆಂಡ ಕಚ್ಚ್ ಡ್ ತೆರಿನ ಒಂಜೇ ಒಂಜಿ ಮೆಗಾಲಿಥಿಕ್ ತಾಣ ಧನೆತಿ ಗ್ರಾಮದ ಸಮೀಪೊಡು ಉಂಡು. ಇತ್ತೆ ಮುಟ್ಟ ತೋಜುದು ಬತ್ತಿನ ಬೇತೆ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ತಾಣೊಲು ಹರಪ್ಪಾ, [[ಮೊಹೆಂಜೊದಾರೊ,]] ಗಣೇರಿವಾಳ, ರಾಖಿಗಡಿ, ಕಾಳಿಬಂಗನ್, ರೂಪನಗರ ಬೊಕ್ಕ ಲೋಥಲ್. ಜುಲೈ ೨೭, ೨೦೨೧ಡ್ ಧೋಲಾವಿರ: ಒಂಜಿ ಹರಪ್ಪನ್ ನಗರ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಪುದರ್ ದೀಯೆರ್ ==ಉತ್ಖನನೊಲು== ಬಿಷ್ಟ್ ನ ನಿರ್ದೇಶನೊಡು ಎಎಸ್ಐ 1989ಡ್ ಉತ್ಖನನ ಸುರು ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ 1990 ಬೊಕ್ಕ 2005 ನಡುಟು 13 ಕ್ಷೇತ್ರ ಉತ್ಖನನೊಲು ನಡತ್ಂಡ್. ಉತ್ಖನನ ನಗರ ಯೋಜನೆ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಬೊಲ್ಪುಗು ಕನೆತೆರ್, ಬೊಕ್ಕ ಪ್ರಾಣಿಲೆನ ಮೂಳೆಲು, ಚಿನ್ನ, ಬೊಳ್ಳಿ, ಟೆರ್ನಾಕೊಟರ್, ಬೊಕ್ಕ ಕಂಚಿದಂಚಿನ ಮಸ್ತ್ ಸಂಖ್ಯೆಡ್ ಪ್ರಾಚೀನ ವಸ್ತುಲೆನ್ ಕಂಡು ಕನತ್ಂಡ್. ದಕ್ಷಿಣ ಗುಜರಾತ್, ಸಿಂಧ್ ಬೊಕ್ಕ ಪಂಜಾಬ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿ ಏಷ್ಯಾದ ವಸತಿ ಸ್ಥಾನೊಲೆನ ನಡುಟು ಧೋಲಾವಿರ ಒಂಜಿ ಪ್ರಮುಖ ವ್ಯಾಪಾರ ಕೇಂದ್ರ ಆದಿತ್ತ್ಂಡ್ ಪಂದ್ ಪುರಾತತ್ತ್ವಜ್ಞೆರ್ ನಂಬುವೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== [[ಲೋಥಲ್]] ಬಂದರ್-ನಗರೊಡ್ದ್ ಪಿರಾಕ್ ದವು ಪನ್ಪಿನ ಅಂದಾಜಿಡ್, ಧೋಲಾವಿರಾ ನಗರೊಗು ಆಯತಾಕಾರೊದ ಆಕಾರೊ ಬೊಕ್ಕ ಸಂಘಟನೆ ಉಂಡು, ಬೊಕ್ಕ ಉಂದು 22 ಹೆಕ್ಟೇರ್ (54 ಎಕ್ರೆ) ಡ್ ವ್ಯಾಪಿಸಾಯಿನವು. ಈ ಪ್ರದೇಶೊದ ಉದ್ದ 771.1 ಮೀ (2,530 ಅಡಿ), ಬೊಕ್ಕ ಅಗಲ 616.85 ಮೀ (2,023.8 ಅಡಿ) ಉಂಡು. ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊದಾರೊದಲೆಕ ಅತ್ತ್, ಈ ನಗರೊನು ಮೂಜಿ ವಿಭಾಗೊಲೆನ್ ಸೇರ್ದಂಚಿನ ಒಂಜಿ ದುಂಬೇ ಇತ್ತಿನ ಜ್ಯಾಮಿತೀಯ ಯೋಜನೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ - ಮಿತ್ತ ಕೋಟೆ,ನಡುತ್ತ ಊರು, ಬೊಕ್ಕ ತಿರ್ತದ ಊರು. ಅಕ್ರೊಪೊಲಿಸ್ ಬೊಕ್ಕ ನಡುತ್ತ ಊರು ಅಕ್ಲೆನ ಸ್ವಂತ ರಕ್ಷಣಾ ಕೆಲಸ, ದ್ವಾರೊಲು, ನಿರ್ಮಾಣ ಆಯಿನ ಪ್ರದೇಶೊಲು,ಸಾದಿ ವ್ಯವಸ್ಥೆ,ಗುವೆಲ್ ಬೊಕ್ಕ ಮಲ್ಲ ತೆರೆದಿಪ್ಪುನ ಜಾಗೊಲೆನ್ ಸಜ್ಜು ಮಲ್ತೆರ್.ಉಂದೆಟ್ ನೈಋತ್ಯ ವಲಯೊದ ಪ್ರಮುಖ ಭಾಗೊನು ಸ್ವಾಧೀನ ಮಲ್ಪುಂಡು. ಎತ್ತರದ "ಕೋಟೆ" ನಿಲೆಲೆನ್ ಡಬಲ್ ರಾಂಪಾರ್ಟ್ ಲೆಡ್ ರಕ್ಷಣೆ ಮಲ್ಪುಂಡು. ಉಂದೆತ ಬರಿಟ್ 'ಬೈಲಿ' ಪನ್ಪಿನ ಜಾಗೆ ಉಂಡು ಅಲ್ಪ ಪ್ರಮುಖ ಅಧಿಕಾರಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಸಾಮಾನ್ಯ ಕೋಟೆಲೆನ ಉಳಯಿ ಉಪ್ಪುನ ನಗರ ಪ್ರದೇಸೊ 48 ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಉಂಡು. ಕೋಟೆದ ವಸಾಹತುಗು ಅವಿಭಾಜ್ಯವಾದ್ ಉಪ್ಪುನ ವಿಸ್ತಾರವಾದ್ ರಚನೆ-ಬೇರಿಂಗ್ ಪ್ರದೇಶೊಲು ಉಂಡು. ಗೋಡೆಲೆನ ಮಿತ್ತ್, ಬೇತೆ ಒಂಜಿ ವಸಾಹತು ತೋಜಿದ್ಬತ್ತ್ಂಡ್. ನಗರೊದ ಅತ್ಯಂತ ಗಮನಾರ್ಹ ಲಕ್ಷಣ ಪಂಡ ಅಯಿತ ಮಾತಾ ಕಟ್ಟಡೊಲು, ಕನಿಷ್ಠ ಅಯಿತ ಇತ್ತೆದ ಸಂರಕ್ಷಣೆದ ಸ್ಥಿತಿಡ್, ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್, ಆಂಡ ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊ-ದಾರೊ ಸೇರ್ದ್ ಬೇತೆ ಹರಪ್ಪನ್ ತಾಣೊಲು ಮಾತಲಾ ಇಟ್ಟಿಗೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಧೋಲಾವಿರಾ ರಡ್ಡ್ ತುಫಾನ್ ನೀರ್ದ ಕಾಲುವೆಲೆಡ್ದ್ ಸುತ್ತುವರಿದ್ ಉಂಡು; ಉತ್ತರೊಡು ಮನ್ಸರ್, ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹರ್. ಊರುದ ಚೌಕೊಡು, ನೆಲತ ಮಿತ್ತ್ ಎತ್ತರೊಡು ಒಂಜಿ ಜಾಗೆ ಉಂಡು, ಅವೆನ್ "ಕೋಟೆ" ಪನ್ಪೆರ್.<ref>https://web.archive.org/web/20170812062930/https://archaeologynewsnetwork.blogspot.com/2010/06/rise-and-fall-of-harappan-city.html</ref> ==ಜಲಾಶಯೊಲು== ಎಎಸ್ಐ ದ ಜಂಟಿ ಮಹಾನಿರ್ದೇಶಕೆರಾದ್ ನಿವೃತ್ತರಾಯಿನ ಬಿಷ್ತ್ ಪನ್ಪೆರ್, "ನೀರ್ ದ ಸಂರಕ್ಷಣೆ, ಕೊಯ್ಲು ಬೊಕ್ಕ ಸಂಗ್ರಹೊಗು ಅಭಿವೃದ್ಧಿ ಮಲ್ತಿನ ಧೋಲಾವಿರದ ಹರಪ್ಪನೆರೆನ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಸ್ಥೆ ಅಕ್ಲೆನ ಸುಧಾರಿತ ಹೈಡ್ರಾಲಿಕ್ ಎಂಜಿನಿಯರಿಂಗ್ ದ ಬಗ್ಗೆ ಪಾತೆರುಂಡು, ಕ್ರಿ.ಪೂ. ಮೂಜಿನೆ ಸಹಸ್ರಮಾನೊಡು ತಂತ್ರಜ್ಞಾನದ ಸ್ಥಿತಿನ್ ತೂಂಡ ಡೊಲಾವಿರಾದ ಸೋಫ್ಯಾಸ್ಟಿಕ್ ನೀರ್ ದ ಅನನ್ಯ ಲಕ್ಷಣೊಲು ಒಂಜಿ." ಚಾನೆಲ್ ಬೊಕ್ಕ ಜಲಾಶಯೊಲೆನ ಸಂರಕ್ಷಣೆದ ವ್ಯವಸ್ಥೆ, ಜಗತ್ತ್ಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಸುರುತ ವ್ಯವಸ್ಥೆ, ಉಂದು ಪೂರ್ತಿ ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ನಗರೊಡು ಮಲ್ಲ ಮಲ್ಲ ಜಲಾಶಯೊಲು ಇತ್ತ್ಂಡ್, ಅವುಲು ಮೂಜಿ ಜಲಾಶಯೊಲು ಬಹಿರಂಗ ಆತ್ಂಡ್. ಅವು ರಡ್ಡ್ ಗಿಟ್ಟದ ಕೆರೆಕುಲೆಡ್ದ್ ಬೇತೆ ಬೇತೆ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಬರ್ಸೊಡ್ದ್ ಬತ್ತಿನ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಆಪುಂಡು. ಉಂದು ಸ್ಪಷ್ಟವಾದ್ ಕಚ್ ದ ಮರುಭೂಮಿದ ಹವಾಮಾನ ಬೊಕ್ಕ ಪರಿಸ್ಥಿತಿಲೆಗ್ ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ಬತ್ತ್ಂಡ್, ಅಲ್ಪ ಕೆಲವು ವರ್ಷೊಲು ಬರ್ಸ ಬರ್ಪುಜಿ. ಈ ಜಾಗೆದ ಸಮೀಪೊಡು ಉತ್ತರ-ದಕ್ಷಿಣ ದಿಕ್ಕ್ ಡ್ ಪೋಪಿನ ಒಂಜಿ ಋತುಕಾಲಿಕ ನೀರ್ ನ್ ನೀರ್ ಸಂಗ್ರಹ ಮಲ್ಪರೆ ಕೆಲವು ಕಡೆಟ್ ಅಣೆಕಟ್ಟ್ ಮಲ್ತೆರ್. ೧೯೯೮ಡ್, ಈ ಜಾಗೆಡ್ ಬೇತೆ ಒಂಜಿ ಜಲಾಶಯೊನು ಕಂಡು ಕನತ್ ದೆರ್.<ref>https://www.cntraveller.in/magazine-story/india-via-gujarat/</ref> ಧೋಲಾವಿರಾದ ನಿವಾಸಿಲು ಮೂಜಿನೆ ಹಂತೊಡು ಬೇತೆ ಬೇತೆ ಗಾತ್ರೊದ ಪದಿನಾಜಿ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ಜಲಾಶಯೊಲೆನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಅವುಲು ಕೆಲವು ಮಲ್ಲ ವಸಾಹತುದ ತಿರ್ತ್ ನೆಲತ ಇಳಿಜಾರು ಪ್ರದೇಸೊದ ಲಾಭ ದೆತೊನುವೆರ್ ಅವುಲು ಗುವೆಲ್ದಂಚಿನ ರಚನೆ ಮಲ್ಪುವೆರ್.. ಬೇತೆ ಜಲಾಶಯೊಲೆನ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಕೆಲವು ಕಲ್ಲ್ಡ್ ಕಟ್ಟ್ದೆರ್ . ಇತ್ತೆದ ಕೆಲಸೊಡು ರಡ್ಡ್ ಮಲ್ಲ ಜಲಾಶಯೊಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್, ಒಂಜಿ ಕೋಟೆದ ಪೂರ್ವೊಡ್ ಬೊಕ್ಕೊಂಜಿ ಅಯಿತ ತೆಂಕ್ ದಿಕ್ಕ್ ಗ್ ಅನೆಕ್ಸ್ ದ ಸಮೀಪೊಡು. ಜಲಾಶಯೊಲು ಕಲ್ಲ್ ಡ್ದ್ ಲಂಬವಾದ್ ಕಡ್ತ್ ದ್ ಉಪ್ಪುಂಡು, ಬೊಕ್ಕ ಅವು ಸುಮಾರ್ 7 ಮೀ (23 ಅಡಿ) ಆಳೊಡು ಬೊಕ್ಕ 79 ಮೀ (259 ಅಡಿ) ಉದ್ದೊಡು ಉಪ್ಪುಂಡು. ಅವು ನಗರೊನು ಸುತ್ತುವರಿದ್ ಉಪ್ಪುಂಡು, ಆಂಡ ಕೋಟೆ ಬೊಕ್ಕ ಮೀಪುನ ಸ್ನಾನಘಟ್ಟಡ್ ಕೇಂದ್ರೊಡು ಎತ್ತರದ ನೆಲಟ್ ಉಪ್ಪುಂಡು. ಒಂಜಿ ಮಲ್ಲ ಗೂವೆಲ್ ಲಾ ಉಂಡು, ಉಂದೆಟ್ ಒಂಜಿ ಕಲ್ಲ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ಟ್ರಫ್ ನ್ ಒಂಜಿ ಡ್ರೈನ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು, ಉಂದು ಒಂಜಿ ಟ್ಯಾಂಕ್ ಗ್ ನೀರ್ ನ್ ಕನಪುಂಡು.ಮೀಪುನ ಜಾಗೆಗ್ ಬರ್ಪಿನವುಲು ಮೆಟ್ಟಿಲುಲು ಉಪ್ಪುಂಡು.<ref>https://whc.unesco.org/en/tentativelists/5892/</ref> ಅಕ್ಟೋಬರ್ ೨೦೧೪ಡ್, ಒಂಜಿ ಆಯತಾಕಾರದ ಮುಟ್ಟ್ ಉಪ್ಪುನ ಗುವೆಲ್ಡ್ ಉತ್ಖನನ ಸುರು ಆಂಡ್, ಉಂದು ೭೩.೪ ಮೀ (೨೪೧ ಅಡಿ) ಉದ್ದ, ೨೯.೩ ಮೀ (೯೬ ಅಡಿ) ಅಗಲ, ಬೊಕ್ಕ ೧೦ ಮೀ (೩೩ ಅಡಿ) ಆಳೊಡು ಇತ್ತ್ಂಡ್, ಉಂದು ಮೊಹೆಂಜೇದಾರೊದ ಮಲ್ಲ ಸ್ನಾನಘಟ್ಟೊಡ್ದ್ ಮೂಜಿ ಪಾಲ್ ಮಲ್ಲವು. ==ಮುದ್ರೆ ಮಲ್ಪುನ== ಧೋಲಾವಿರಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಕೆಲವು ಮುದ್ರೆಲು, ಸ್ಟೇಜ್ III ಗ್ ಸೇರ್ದಿನವು, ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಮಾತ್ರ ಮೂಲು ತೂವೋಲಿ ಅತ್ತಂದೆ, ಒವ್ವೆ ರೀತಿದ ಲಿಪಿ ಇಜ್ಜಿ. ಈ ರೀತಿದ ಮುದ್ರೆಲು ಸಿಂಧು ಮುದ್ರೆ ತಯಾರಿಕೆದ ಪಿರಾಕ್ ದ ಸಂಪ್ರದಾಯೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ==ಬೇತೆ ರಚನೆಲು ಬೊಕ್ಕ ವಸ್ತುಲು== ==ಪೂರ್ವೊದ ದ್ವಾರ== ಆ ಜಾಗೆಡ್ ಒಂಜಿ ಮಲ್ಲ ವೃತ್ತಾಕಾರದ ರಚನೆ ಒಂಜಿ ಸಮಾಧಿ ಅತ್ತಂಡ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ನಂಬುನವು ತಿಕ್ಕ್ಂಡ್, ಆಂಡಲಾ ಅಯಿಟ್ ಅಸ್ಥಿಪಂಜರೊಲು ಅತ್ತಂಡ ಬೇತೆ ಮಾನವ ಅವಶೇಷೊಲು ಇಜ್ಜಿ. ಈ ರಚನೆಡ್ ಒಂಜಿ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ಆಕಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಪತ್ತ್ ರೇಡಿಯಲ್ ಮಣ್ಣ್-ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು ಉಂಡು. ಫಾಲಸ್ ಎರೆಕ್ಟಸ್ ಆಂಡ ತರೆ ಬೊಕ್ಕ ಕಾರ್ ಪಾದೊದ ತಿರ್ತ್ ಕತ್ತರಿಸಾಯಿನ ಒಂಜಿ ಪುರುಷನ ಮೃದುವಾಯಿನ ಮರಳುಕಲ್ಲುದ ಶಿಲ್ಪೊನು ಪೂರ್ವ ದ್ವಾರೊದ ಮಾರ್ಗೊಡು ತೂಯೆರ್. ಮಸ್ತ್ ಶವಸಂಸ್ಕಾರದ ರಚನೆಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ (ಒಂಜಿ ಬುಕ್ಕೊ ಮಾತಾ ಅಸ್ಥಿಪಂಜರೊಲು ಇಜ್ಜಿ), ಅಂಚನೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಟೆರಾಕೋಟಾ ಮುದ್ರೆಲು, ಬಳೆ, ಉಂಗುರೊಲು, ಮಣಿ, ಬೊಕ್ಕ ಕೆತ್ತನೆ. ಡಾನ್ಸಿಂಗ್ ಗರ್ಲ್ ಪಂಡ ಹರಪ್ಪನ್ ನಾಗರಿಕತೆದ ಅತ್ಯಂತ ಪ್ರತಿಷ್ಠಿತ ಕಲಾಕೃತಿಲೆಡ್ ಒಂಜಿ, ಮೊಹೆಂಜೊದಾರೊಡು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಮಾರ್ ಕ್ರಿ.ಪೂ. 2500 ಡ್ದ್ ದುಂಬುದ ಈ ಕಂಚಿದ ಪ್ರತಿಮೆ ಸುಮಾರ್ 10.5 ಸೆಂಟಿಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು ಬೊಕ್ಕ ಒರಿ ಜವನೆರೆನ ನರ್ತಕಿನ ಕ್ರಿಯಾತ್ಮಕ ಶಕ್ತಿ ಬೊಕ್ಕ ಅನುಗ್ರಹೊನು ಸ್ಪಷ್ಟವಾದ್ ಸೆರೆಪತ್ತ್ದ್ಂಡ್. ==ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು== [[File:Cicular houses Dholavira.jpg|thumb|ಧೋಲಾವಿರದ ಗೋಲಾಕಾರದ ಇಲ್ಲುಲು - ಹರಪ್ಪನ್ ಊರು]] ಧೋಲಾವಿರಾಡ್ ಏಳ್ ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲೆನ್ ತೂಯೆರ್, ಅವುಲು ರಡ್ಡ್ ನ್ ವಿವರವಾದ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಅವು ಮಲ್ಲ ಕಲ್ಲ್ ಕತ್ತರಿಸಾಯಿನ ಕೋಣೆಲೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಯೋಜನೆನ್ ಪಡೆಯಿನವು, ಅವು ಮಲ್ಲ ಅರ್ಧಗೋಳಾಕಾರದ ಎತ್ತರದ ಮಣ್ಣ್ ದ ಇಟ್ಟಿಗೆದ ನಿರ್ಮಾಣೊಲು. ಉತ್ಖನನ ಮಲ್ತಿನ ಒಂಜಿ ರಚನೆನ್ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೂಪೊಡು ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕೊಂಜಿಲಾ ಅದೇ ರೀತಿಡ್ ಡಿಸೈನ್ ಮಲ್ತೆರ್, ಆಂಡ ಸ್ಪೋಕ್ಸ್ ಇಜ್ಜಂದಿನ ಚಕ್ರೊದಲೆಕ. ಅವುಲು ಕುಂಬಾರಿಕೆದ ಸಮಾಧಿದ ವಸ್ತುಲು ಇತ್ತ್ಂಡಲಾ, ಒಂಜಿ ಸಮಾಧಿನ್ ಬುಡ್ದು ಬೇತೆ ಒವ್ವೆ ಅಸ್ಥಿಪಂಜರೊಲು ತೋಜಿಜಿ, ಅಲ್ಪ ಒಂಜಿ ಅಸ್ಥಿಪಂಜರ ಬೊಕ್ಕ ಒಂಜಿ ತಾಮ್ರದ ಕನ್ನಡಿ ಉಂಡು. ಒಂಜಿ ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಡ್ ಒಂಜಿ ತಾಮ್ರದ ತಂತಿಗ್ ಕೊಕ್ಕೆಲೆನ್ ಕಟ್ಟ್ ದ್ ದೀತಿನ ಸ್ಟೀಟೈಟ್ ಮಣಿಲೆನ ಕಂಕಣ, ಒಂಜಿ ಬಂಗಾರ್ದ ಬಳೆ, ಬಂಗಾರ್ ಬೊಕ್ಕ ಬೇತೆ ಮಣಿಕುಲುಲಾ ಒಂಜಿ ಅರ್ಧಗೋಳದ ರಚನೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಉತ್ಖನನೊನು ನಡಪಾಯಿನ ಭಾರತದ ಪುರಾತತ್ವ ಸಮೀಕ್ಷೆ, ಈ "ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಲು ಪಿರಾಕ್ದ ಬೌದ್ಧ ಸ್ತೂಪೊಲೆನ್ ನೆನಪು ಮಲ್ಪುಂಡು" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ಪನ್ಪುಂಡು. ಬೊಕ್ಕ "ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ಬೊಕ್ಕ ಅನ್ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೀತಿದ ವಿನ್ಯಾಸ ಶತಪಥ ಬ್ರಾಹ್ಮಣ ಬೊಕ್ಕ ಶುಲ್ಬ ಸೂತ್ರೊಡು ನಮೂದಿಸಾಯಿನ ಸರರತ-ಚಕ್ರ-ಸಿಟಿ ಬೊಕ್ಕ ಸಪ್ರಧಿ-ರತ-ಚಕ್ರ-ಸಿಟಿನ್ ನೆಂಪು ಮಲ್ಪುಂಡು" ==ಉತ್ತರೊದ ದ್ವಾರ== [[File:Dholavira-1.jpg|thumb|ಧೋಲಾವಿರ-1]] ಸಿಂಧು ಲಿಪಿತ ಮಿತ್ತ್ ಕಪ್ಪು ಬಣ್ಣದ ಕುಂಬಾರಿಕೆ, ಚೌಕ ಮುದ್ರೆದ ಮುದ್ರೆಲು, ಸಿಂಧು ಲಿಪಿ ಇಜ್ಜಂದಿನ ಮುದ್ರೆಲು, ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದೊದ ಒಂಜಿ ಮಲ್ಲ ಸೈನ್ ಬೋರ್ಡ್, ಸಿಂಧು ಲಿಪಿತ ಪತ್ತ್ ಅಕ್ಷರೊಲೆನ್ ಸೇರ್ದುಂಡು. ಕಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಒಂಜಿ ಕಳಪೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಕುಲ್ಲುನ ಪುರುಷ ರೂಪೊನುಲಾ ತೂಯೆರ್, ಉಂದು ಹರಪ್ಪಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಉನ್ನತ ಗುಣಮಟ್ಟದ ರಡ್ಡ್ ಕಲ್ಲ್ ದ ಶಿಲ್ಪೊಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಮುಳ್ಳುದ ನೆಲತ ಮಲ್ಲ ಕಪ್ಪು ಜಾರ್ ಲೆನ್ ಲಾ ಈ ಜಾಗೆಡ್ ತೂಯೆರ್. ಒಂಜಿ ಮಲ್ಲ ಕಂಚಿದ ಸುತ್ತಿಗೆ, ಒಂಜಿ ಮಲ್ಲ ಉಳಿ, ಒಂಜಿ ಕಂಚಿನ ಕೈಟ್ ಪತ್ತೊಂದಿಪ್ಪುನ ಕನ್ನಡಿ, ಒಂಜಿ ಬಂಗಾರ್ದ ತಂತಿ, ಬಂಗಾರ್ದ ಕೆಬಿ ಓಲೆ, ತಾಮ್ರದ ಸೆಲ್ಟ್ಸ್ ಬೊಕ್ಕ ಚಿಪ್ಪುದ ಬಳೆ, ಕಲ್ಲ್ ದ ಫಾಲಸ್ ಲೆಕೊದ ಚಿಹ್ನೆಲು, ಸಿಂಧು ಶಾಸನ ಬೊಕ್ಕ ಚಿಹ್ನೆಲೆನ್ ದೀದ್ ಚೌಕ ಮುದ್ರೆಲು, ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಮುದ್ರೆ, ಕುಂಟುದ ಪ್ರಾಣಿಲು, ಪಾಟರ್ ಪೇಂಟೆಡ್ ಪಾಟರ್ಡ್, ಗೊಬ್ಬುಲು, ಡಿಶ್-ಆನ್-ಸ್ಟ್ಯಾಂಡ್, ರಂದ್ರೊಡುಪ್ಪುನ ಜಾರ್ ಲು, ಎಡ್ಡೆ ಆಕಾರೊಡು ಉಪ್ಪುನ ಟೆರಾಕೊಟಾ ಟಂಬ್ಲರ್ ಲು,ಕಡೆಪ್ಪುನ ಕಲ್ಲ್, ಗಾರೆಲು ಇಂಚಿನವುಲಾ ಈ ಜಾಗೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಬೇತೆ ಬೇತೆ ಅಳತೆದ ಕಲ್ಲ್ ದ ತೂಕೊಲೆನ್ ಲಾ ತೂಯೆರ್. ==ಕರಾವಳಿ ಮಾರ್ಗ== ಲೋಥಲ್ ಬೊಕ್ಕ ಧೋಲಾವಿರಾನ್ ಮಕ್ರಾನ್ ಕರಾವಳಿಡ್ ಸುತ್ಕಗನ್ ದೋರ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುನ ಒಂಜಿ ಕರಾವಳಿ ಮಾರ್ಗ ಉಂಡು. ==ಬಾಸೆ ಬೊಕ್ಕ ಲಿಪಿ == ಹರಪ್ಪನೆರ್ ಒಂಜಿ ಅಜ್ಞಾತ ಬಾಸೆನ್ ಪಾತೆರೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಅಕ್ಲೆನ ಲಿಪಿನ್ ಇತ್ತೆಲಾ ಅರ್ಥ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಉಂದೆಟ್ ಸುಮಾರ್ 400 ಮೂಲ ಚಿಹ್ನೆಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ನಂಬಿಕೆ ಉಂಡು, ಉಂದೆಟ್ ಮಸ್ತ್ ಬದಲಾವಣೆಲು ಉಂಡು. ಚಿಹ್ನೆಲು ಪದೊಕುಲೆಗ್ ಬೊಕ್ಕ ಉಚ್ಚಾರೊಲೆಗ್ ನಿಲೆಯಾದಿತ್ತ್ ದ್ ಉಪ್ಪೊಲಿ. ಬರವುದ ದಿಕ್ಕು ಸಾಮಾನ್ಯವಾದ್ ಬಲತ್ತ ಕಡೆಟ್ದ್ ಎಡತ್ತ್ ಗ್ ಇತ್ತ್ಂಡ್. ಹೆಚ್ಚಿನ ಶಾಸನೊಲು ಮುದ್ರೆಲೆಡ್ (ಬಹುತೇಕ ಕಲ್ಲ್ ಡ್ದ್ ಮಲ್ಪುನವು) ಬೊಕ್ಕ ಮುದ್ರೆಲೆಡ್ (ಮಣ್ಣ್ ದ ತುಂಡುಲೆಡ್ ಮುದ್ರೆನ್ ಒತ್ತುದು ಅಯಿತ ಗುರುತುನ್ ಬುಡ್ಪೆರ್) ಉಪ್ಪುಂಡು. ಕೆಲವು ಶಾಸನೊಲು ತಾಮ್ರದ ಫಲಕೊಲೆಡ್, ಕಂಚಿದ ಉಪಕರಣೊಲೆಡ್, ಬೊಕ್ಕ ಟೆರಾಕೊಟಾ, ಕಲ್ಲ್ ಬೊಕ್ಕ ಫೇಯೆನ್ಸ್ ಡ್ ಮಲ್ತಿನ ಎಲ್ಯ ವಸ್ತುಲೆಡ್ಲಾ ತೋಜುಂಡು. ಈ ಮುದ್ರೆಲೆನ್ ವ್ಯಾಪಾರೊಡು ಬೊಕ್ಕ ಅಧಿಕೃತ ಆಡಳಿತ ಕೆಲಸೊಗುಲಾ ಬಳಕೆ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಮೊಹೆಂಜೊದಾರೊ ಬೊಕ್ಕ ಬೇತೆ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ಜಾಗೆಲೆಡ್ ಮಸ್ತ್ ಕೆತ್ತನೆದ ಸಾಮಗ್ರಿಲೆನ್ ತೂಯೆರ್.<ref>https://web.archive.org/web/20110903114242/http://asi.nic.in/asi_exca_2007_dholavira.asp</ref> ==ಸೈನ್ ಬೋರ್ಡ್== [[File:The 'Ten Indus Scripts' discovered near the northern gateway of the Dholavira citadel.jpg|thumb|Ten Indus characters from the northern gate of Dholavira, dubbed the Dholavira Signboard, one of the longest known sequences of Indus characters]] ಧೋಲಾವಿರೊಡು ಮಸ್ತ್ ಮಹತ್ವದ ಅನ್ವೇಷಣೆಲು ನಗರೊದ ಉತ್ತರ ದ್ವಾರೊದ ಒಂಜಿ ಸೈಡ್ ರೂಮ್ ಡ್ ನಡತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಧೋಲಾವಿರ ಸೈನ್ಬೋರ್ಡ್ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ಹರಪ್ಪೆರ್ ಖನಿಜ ಜಿಪ್ಸಮ್ ದ ತುಂಡುಲೆನ್ ಒಂಜಿ ಮಲ್ಲ ಮರತ್ತ ಫಲಕೊಡು ಪತ್ತ್ ಮಲ್ಲ ಚಿಹ್ನೆಲೆನ್ ಅತ್ತಂಡ ಅಕ್ಷರೊಲೆನ್ ರೂಪಿಸೆರೆ ಜೋಡಾದ್ ದೀತೆರ್. ಒಂಜಿ ಸರ್ತಿ, ಫಲಕ ಅಯಿತ ಮೋನೆಡ್ ಫ್ಲಾಟ್ ಆದ್ ಬೂರುಂಡು. ಮರ ಕುಸಿತ್ಂಡ್, ಆಂಡ ಅಕ್ಷರೊಲೆನ ವ್ಯವಸ್ಥೆ ಒರಿದ್ಂಡ್. ಸೈನ್ ಬೋರ್ಡ್ ದ ಅಕ್ಷರೊಲು ಸುತ್ತಮುತ್ತದ ಗೋಡೆಲೆಡ್ ಬಳಕೆ ಆಯಿನ ಮಲ್ಲ ಇಟ್ಟಿಗೆಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಪ್ರತಿಯೊಂಜಿ ಚಿಹ್ನೆ ಸುಮಾರ್ 37 ಸೆಂ (15 ಇಂಚ್) ಎತ್ತರೊಡು ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಬೋರ್ಡ್ ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದ ಉಪ್ಪುಂಡು. ಈ ಶಾಸನ ಸಿಂಧು ಲಿಪಿಟ್ ಅತಿ ಉದ್ದೊದ ಶಾಸನೊಲೆಡ್ ಒಂಜಿ, ಒಂಜಿ ಚಿಹ್ನೆ ನಾಲ್ ಸರ್ತಿ ತೋಜುಂಡು, ಬೊಕ್ಕ ಅಯಿತ ಮಲ್ಲ ಗಾತ್ರ ಬೊಕ್ಕ ಸಾರ್ವಜನಿಕ ಸ್ವಭಾವೊ ಸಿಂಧು ಲಿಪಿ ಪೂರ್ತಿ ಸಾಕ್ಷರತೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಈ ಜಾಗೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ಲ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಒಂಜಿ ನಾಲ್ ಚಿಹ್ನೆದ ಶಾಸನೊನುಲಾ ತೂವೊಲಿ, ಉಂದು ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಇಂಚಿನ ಶಾಸನೊಲೆಡ್ ಸುರುತವು ಆದುಂಡು ಪನ್ಪೆರ್. [[ವರ್ಗ:Event:Heritage Across Borders]] digz2s8uimlrp8p4j542q7pyeirug78 366199 366198 2026-08-21T10:23:59Z Kavitha G. Kana 1040 366199 wikitext text/x-wiki [[File:DHOLAVIRA SITE (24).jpg|thumb|ಧೋಲಾವಿರ ಜಾಗ್ವ್ (24)]] ಧೋಲಾವಿರ ಪಂಡ ಪಶ್ಚಿಮ ಭಾರತೊದ [[ಗುಜರಾತ್]] ರಾಜ್ಯೊದ [[ಕಚ್]] ಜಿಲ್ಲೆದ ಭಾಚೌ ತಾಲೂಕುದ ಖಾದಿರ್ಬೆಟ್ ಡ್ ಉಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಜಾಗೆ, ಉಂದೆತ ಪುದರ್ ದಕ್ಷಿಣೊಡು 1 ಕಿಲೋಮೀಟರ್ (0.62 ಮೈಲ್) ದೂರೊಡು ಉಪ್ಪುನ ಇತ್ತೆದ ಗ್ರಾಮೊದ ಪುದರ್ ಡ್ ಉಂಡು. ಈ ಊರು ರಾಧನ್ಪುರ್ ಡ್ದ್ 165 ಕಿ.ಮೀ (103 ಮೈಲ್) ದೂರೊಡು ಉಂಡು. ಸ್ಥಳೀಯವಾದ್ ಕೋಟದ ಟಿಂಬಾ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಈ ಜಾಗೆಡ್ ಪ್ರಾಚೀನ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ನಗರೊದ ಅವಶೇಷೊಲು ಉಂಡು. ಭೂಕಂಪೊಲು ಧೋಲಾವಿರ ಪನ್ಪಿನ ಈ ಜಾಗೆಗ್ ಸುಮಾರ್ ಸರ್ತಿ ಅಯಿತ ಕೆಟ್ಟ ಪರಿಣಾಮೊನ್ ಬೀರ್ದ್ಂಡ್ .<ref>https://www.thehindu.com/society/ruins-on-the-tropic-of-cancer/article19284214.ece</ref> ==ಜಾಗೆ== ಧೋಲಾವಿರ ಪನ್ಪಿನ ಜಾಗೆ ಉಷ್ಣವಲಯೊಡು(Tropic of cancer) ಉಂಡು. ಉಂದು ಐನ್ ಮಲ್ಲ ಹರಪ್ಪನ್ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಬೊಕ್ಕ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆಗ್ ಸೇರ್ದಿನ ಭಾರತೊದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಪಿರಾಕ್ದ ತಾಣ. ಉಂದು ಕಚ್ ಮರುಭೂಮಿದ ವನ್ಯಜೀವಿ ಅಭಯಾರಣ್ಯೊಡು ಕಚ್ ಗ್ರೇಟ್ ರನ್ ಡ್ ಖಾದಿರ್ ಬೆಟ್ ದ್ವೀಪೊಡು ಉಂಡು. ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಚತುರ್ಭುಜಾಕೃತಿದ ನಗರ ಉತ್ತರೊಡು ಮನ್ಸಾರ್ ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹಾರ್ ಪನ್ಪಿನ ಸುದೆತ್ತ ನಡುಟು ಉಂಡು. ಕ್ರಿ.ಪೂ ೧೪೫೦; ಆಂಡಲಾ, ಇತ್ತೆದ ಸಂಶೋಧನೆ ಸುಮಾರ್ ಕ್ರಿ.ಪೂ.೩೫೦೦ಡ್ (ಹರಪ್ಪನ್ ಪೂರ್ವ) ಆಕ್ರಮಣದ ಸುರುತ ಬೊಕ್ಕ ಕ್ರಿ.ಪೂ ೧೮೦೦ (ಹರಪ್ಪನ್ ಕಾಲೊದ ಸುರುತ ಭಾಗ) ಮುಟ್ಟ ನಿರಂತರತೆನ್ ಸೂಚಿಸವುಂಡು ==ಅನ್ವೇಷಣೆ== ಈ ಜಾಗೆನ್ ಸುರುಕು ಧೋಲಾವಿರ ಗ್ರಾಮದ ನಿವಾಸಿ ಶಂಭೂದನ್ ಗಾಧವಿ 1960 ದ ಸುರುತ ಭಾಗೊಡು ಕಂಡು ಕನತ್ದ್, ಸರ್ಕಾರದ ಗಮನೊನು ಈ ಜಾಗೆಗ್ ಕನರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. ಈ ಜಾಗೆನ್ 1967 ಅತ್ತ್ಂಡ 1968ಡ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ದ ಜೆ.ಪಿ.ಜೋಶಿ "ಅಧಿಕೃತವಾದ್" ಬೊಳ್ಪುಗು ಕನತೆರ್, ಬೊಕ್ಕ ಉಂದು ಎಣ್ಮ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಐನ್ ನೇ ಮಲ್ಲ ಜಾಗೆ. ಉಂದು ೧೯೯೦ಡ್ದ್ ಎಎಸ್ಐಡ್ದ್ ಉತ್ಖನನೊಡು ಉಂಡು, ಉಂದು "ಧೋಲಾವಿರ ಸಿಂಧು ಕಣಿವೆದ ನಾಗರಿಕತೆದ ಅರ್ಥ ಮಲ್ಪುನಗ ನಿಜವಾದ್ ಲಾ ಪೊಸ ಆಯಾಮೊಲೆನ್ ಸೇರ್ಸಾದ್ಂಡ್" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ತೋಜಾದ್ಂಡ್. ಧೋಲಾವಿರಾನ್ ಕಚ್ ದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಮೆಗಾಲಿಥಿಕ್(ಶಿಲಾಯುಗ) ತಾಣೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಂಡುಂಡು, ಆಂಡ ಕಚ್ಚ್ ಡ್ ತೆರಿನ ಒಂಜೇ ಒಂಜಿ ಮೆಗಾಲಿಥಿಕ್ ತಾಣ ಧನೆತಿ ಗ್ರಾಮದ ಸಮೀಪೊಡು ಉಂಡು. ಇತ್ತೆ ಮುಟ್ಟ ತೋಜುದು ಬತ್ತಿನ ಬೇತೆ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ತಾಣೊಲು ಹರಪ್ಪಾ, [[ಮೊಹೆಂಜೊದಾರೊ,]] ಗಣೇರಿವಾಳ, ರಾಖಿಗಡಿ, ಕಾಳಿಬಂಗನ್, ರೂಪನಗರ ಬೊಕ್ಕ ಲೋಥಲ್. ಜುಲೈ ೨೭, ೨೦೨೧ಡ್ ಧೋಲಾವಿರ: ಒಂಜಿ ಹರಪ್ಪನ್ ನಗರ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಪುದರ್ ದೀಯೆರ್ ==ಉತ್ಖನನೊಲು== ಬಿಷ್ಟ್ ನ ನಿರ್ದೇಶನೊಡು ಎಎಸ್ಐ 1989ಡ್ ಉತ್ಖನನ ಸುರು ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ 1990 ಬೊಕ್ಕ 2005 ನಡುಟು 13 ಕ್ಷೇತ್ರ ಉತ್ಖನನೊಲು ನಡತ್ಂಡ್. ಉತ್ಖನನ ನಗರ ಯೋಜನೆ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಬೊಲ್ಪುಗು ಕನೆತೆರ್, ಬೊಕ್ಕ ಪ್ರಾಣಿಲೆನ ಮೂಳೆಲು, ಚಿನ್ನ, ಬೊಳ್ಳಿ, ಟೆರ್ನಾಕೊಟರ್, ಬೊಕ್ಕ ಕಂಚಿದಂಚಿನ ಮಸ್ತ್ ಸಂಖ್ಯೆಡ್ ಪ್ರಾಚೀನ ವಸ್ತುಲೆನ್ ಕಂಡು ಕನತ್ಂಡ್. ದಕ್ಷಿಣ ಗುಜರಾತ್, ಸಿಂಧ್ ಬೊಕ್ಕ ಪಂಜಾಬ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿ ಏಷ್ಯಾದ ವಸತಿ ಸ್ಥಾನೊಲೆನ ನಡುಟು ಧೋಲಾವಿರ ಒಂಜಿ ಪ್ರಮುಖ ವ್ಯಾಪಾರ ಕೇಂದ್ರ ಆದಿತ್ತ್ಂಡ್ ಪಂದ್ ಪುರಾತತ್ತ್ವಜ್ಞೆರ್ ನಂಬುವೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== [[ಲೋಥಲ್]] ಬಂದರ್-ನಗರೊಡ್ದ್ ಪಿರಾಕ್ ದವು ಪನ್ಪಿನ ಅಂದಾಜಿಡ್, ಧೋಲಾವಿರಾ ನಗರೊಗು ಆಯತಾಕಾರೊದ ಆಕಾರೊ ಬೊಕ್ಕ ಸಂಘಟನೆ ಉಂಡು, ಬೊಕ್ಕ ಉಂದು 22 ಹೆಕ್ಟೇರ್ (54 ಎಕ್ರೆ) ಡ್ ವ್ಯಾಪಿಸಾಯಿನವು. ಈ ಪ್ರದೇಶೊದ ಉದ್ದ 771.1 ಮೀ (2,530 ಅಡಿ), ಬೊಕ್ಕ ಅಗಲ 616.85 ಮೀ (2,023.8 ಅಡಿ) ಉಂಡು. ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊದಾರೊದಲೆಕ ಅತ್ತ್, ಈ ನಗರೊನು ಮೂಜಿ ವಿಭಾಗೊಲೆನ್ ಸೇರ್ದಂಚಿನ ಒಂಜಿ ದುಂಬೇ ಇತ್ತಿನ ಜ್ಯಾಮಿತೀಯ ಯೋಜನೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ - ಮಿತ್ತ ಕೋಟೆ,ನಡುತ್ತ ಊರು, ಬೊಕ್ಕ ತಿರ್ತದ ಊರು. ಅಕ್ರೊಪೊಲಿಸ್ ಬೊಕ್ಕ ನಡುತ್ತ ಊರು ಅಕ್ಲೆನ ಸ್ವಂತ ರಕ್ಷಣಾ ಕೆಲಸ, ದ್ವಾರೊಲು, ನಿರ್ಮಾಣ ಆಯಿನ ಪ್ರದೇಶೊಲು,ಸಾದಿ ವ್ಯವಸ್ಥೆ,ಗುವೆಲ್ ಬೊಕ್ಕ ಮಲ್ಲ ತೆರೆದಿಪ್ಪುನ ಜಾಗೊಲೆನ್ ಸಜ್ಜು ಮಲ್ತೆರ್.ಉಂದೆಟ್ ನೈಋತ್ಯ ವಲಯೊದ ಪ್ರಮುಖ ಭಾಗೊನು ಸ್ವಾಧೀನ ಮಲ್ಪುಂಡು. ಎತ್ತರದ "ಕೋಟೆ" ನಿಲೆಲೆನ್ ಡಬಲ್ ರಾಂಪಾರ್ಟ್ ಲೆಡ್ ರಕ್ಷಣೆ ಮಲ್ಪುಂಡು. ಉಂದೆತ ಬರಿಟ್ 'ಬೈಲಿ' ಪನ್ಪಿನ ಜಾಗೆ ಉಂಡು ಅಲ್ಪ ಪ್ರಮುಖ ಅಧಿಕಾರಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಸಾಮಾನ್ಯ ಕೋಟೆಲೆನ ಉಳಯಿ ಉಪ್ಪುನ ನಗರ ಪ್ರದೇಸೊ 48 ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಉಂಡು. ಕೋಟೆದ ವಸಾಹತುಗು ಅವಿಭಾಜ್ಯವಾದ್ ಉಪ್ಪುನ ವಿಸ್ತಾರವಾದ್ ರಚನೆ-ಬೇರಿಂಗ್ ಪ್ರದೇಶೊಲು ಉಂಡು. ಗೋಡೆಲೆನ ಮಿತ್ತ್, ಬೇತೆ ಒಂಜಿ ವಸಾಹತು ತೋಜಿದ್ಬತ್ತ್ಂಡ್. ನಗರೊದ ಅತ್ಯಂತ ಗಮನಾರ್ಹ ಲಕ್ಷಣ ಪಂಡ ಅಯಿತ ಮಾತಾ ಕಟ್ಟಡೊಲು, ಕನಿಷ್ಠ ಅಯಿತ ಇತ್ತೆದ ಸಂರಕ್ಷಣೆದ ಸ್ಥಿತಿಡ್, ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್, ಆಂಡ ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊ-ದಾರೊ ಸೇರ್ದ್ ಬೇತೆ ಹರಪ್ಪನ್ ತಾಣೊಲು ಮಾತಲಾ ಇಟ್ಟಿಗೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಧೋಲಾವಿರಾ ರಡ್ಡ್ ತುಫಾನ್ ನೀರ್ದ ಕಾಲುವೆಲೆಡ್ದ್ ಸುತ್ತುವರಿದ್ ಉಂಡು; ಉತ್ತರೊಡು ಮನ್ಸರ್, ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹರ್. ಊರುದ ಚೌಕೊಡು, ನೆಲತ ಮಿತ್ತ್ ಎತ್ತರೊಡು ಒಂಜಿ ಜಾಗೆ ಉಂಡು, ಅವೆನ್ "ಕೋಟೆ" ಪನ್ಪೆರ್.<ref>https://web.archive.org/web/20170812062930/https://archaeologynewsnetwork.blogspot.com/2010/06/rise-and-fall-of-harappan-city.html</ref> ==ಜಲಾಶಯೊಲು== ಎಎಸ್ಐ ದ ಜಂಟಿ ಮಹಾನಿರ್ದೇಶಕೆರಾದ್ ನಿವೃತ್ತರಾಯಿನ ಬಿಷ್ತ್ ಪನ್ಪೆರ್, "ನೀರ್ ದ ಸಂರಕ್ಷಣೆ, ಕೊಯ್ಲು ಬೊಕ್ಕ ಸಂಗ್ರಹೊಗು ಅಭಿವೃದ್ಧಿ ಮಲ್ತಿನ ಧೋಲಾವಿರದ ಹರಪ್ಪನೆರೆನ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಸ್ಥೆ ಅಕ್ಲೆನ ಸುಧಾರಿತ ಹೈಡ್ರಾಲಿಕ್ ಎಂಜಿನಿಯರಿಂಗ್ ದ ಬಗ್ಗೆ ಪಾತೆರುಂಡು, ಕ್ರಿ.ಪೂ. ಮೂಜಿನೆ ಸಹಸ್ರಮಾನೊಡು ತಂತ್ರಜ್ಞಾನದ ಸ್ಥಿತಿನ್ ತೂಂಡ ಡೊಲಾವಿರಾದ ಸೋಫ್ಯಾಸ್ಟಿಕ್ ನೀರ್ ದ ಅನನ್ಯ ಲಕ್ಷಣೊಲು ಒಂಜಿ." ಚಾನೆಲ್ ಬೊಕ್ಕ ಜಲಾಶಯೊಲೆನ ಸಂರಕ್ಷಣೆದ ವ್ಯವಸ್ಥೆ, ಜಗತ್ತ್ಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಸುರುತ ವ್ಯವಸ್ಥೆ, ಉಂದು ಪೂರ್ತಿ ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ನಗರೊಡು ಮಲ್ಲ ಮಲ್ಲ ಜಲಾಶಯೊಲು ಇತ್ತ್ಂಡ್, ಅವುಲು ಮೂಜಿ ಜಲಾಶಯೊಲು ಬಹಿರಂಗ ಆತ್ಂಡ್. ಅವು ರಡ್ಡ್ ಗಿಟ್ಟದ ಕೆರೆಕುಲೆಡ್ದ್ ಬೇತೆ ಬೇತೆ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಬರ್ಸೊಡ್ದ್ ಬತ್ತಿನ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಆಪುಂಡು. ಉಂದು ಸ್ಪಷ್ಟವಾದ್ ಕಚ್ ದ ಮರುಭೂಮಿದ ಹವಾಮಾನ ಬೊಕ್ಕ ಪರಿಸ್ಥಿತಿಲೆಗ್ ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ಬತ್ತ್ಂಡ್, ಅಲ್ಪ ಕೆಲವು ವರ್ಷೊಲು ಬರ್ಸ ಬರ್ಪುಜಿ. ಈ ಜಾಗೆದ ಸಮೀಪೊಡು ಉತ್ತರ-ದಕ್ಷಿಣ ದಿಕ್ಕ್ ಡ್ ಪೋಪಿನ ಒಂಜಿ ಋತುಕಾಲಿಕ ನೀರ್ ನ್ ನೀರ್ ಸಂಗ್ರಹ ಮಲ್ಪರೆ ಕೆಲವು ಕಡೆಟ್ ಅಣೆಕಟ್ಟ್ ಮಲ್ತೆರ್. ೧೯೯೮ಡ್, ಈ ಜಾಗೆಡ್ ಬೇತೆ ಒಂಜಿ ಜಲಾಶಯೊನು ಕಂಡು ಕನತ್ ದೆರ್.<ref>https://www.cntraveller.in/magazine-story/india-via-gujarat/</ref> ಧೋಲಾವಿರಾದ ನಿವಾಸಿಲು ಮೂಜಿನೆ ಹಂತೊಡು ಬೇತೆ ಬೇತೆ ಗಾತ್ರೊದ ಪದಿನಾಜಿ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ಜಲಾಶಯೊಲೆನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಅವುಲು ಕೆಲವು ಮಲ್ಲ ವಸಾಹತುದ ತಿರ್ತ್ ನೆಲತ ಇಳಿಜಾರು ಪ್ರದೇಸೊದ ಲಾಭ ದೆತೊನುವೆರ್ ಅವುಲು ಗುವೆಲ್ದಂಚಿನ ರಚನೆ ಮಲ್ಪುವೆರ್.. ಬೇತೆ ಜಲಾಶಯೊಲೆನ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಕೆಲವು ಕಲ್ಲ್ಡ್ ಕಟ್ಟ್ದೆರ್ . ಇತ್ತೆದ ಕೆಲಸೊಡು ರಡ್ಡ್ ಮಲ್ಲ ಜಲಾಶಯೊಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್, ಒಂಜಿ ಕೋಟೆದ ಪೂರ್ವೊಡ್ ಬೊಕ್ಕೊಂಜಿ ಅಯಿತ ತೆಂಕ್ ದಿಕ್ಕ್ ಗ್ ಅನೆಕ್ಸ್ ದ ಸಮೀಪೊಡು. ಜಲಾಶಯೊಲು ಕಲ್ಲ್ ಡ್ದ್ ಲಂಬವಾದ್ ಕಡ್ತ್ ದ್ ಉಪ್ಪುಂಡು, ಬೊಕ್ಕ ಅವು ಸುಮಾರ್ 7 ಮೀ (23 ಅಡಿ) ಆಳೊಡು ಬೊಕ್ಕ 79 ಮೀ (259 ಅಡಿ) ಉದ್ದೊಡು ಉಪ್ಪುಂಡು. ಅವು ನಗರೊನು ಸುತ್ತುವರಿದ್ ಉಪ್ಪುಂಡು, ಆಂಡ ಕೋಟೆ ಬೊಕ್ಕ ಮೀಪುನ ಸ್ನಾನಘಟ್ಟಡ್ ಕೇಂದ್ರೊಡು ಎತ್ತರದ ನೆಲಟ್ ಉಪ್ಪುಂಡು. ಒಂಜಿ ಮಲ್ಲ ಗೂವೆಲ್ ಲಾ ಉಂಡು, ಉಂದೆಟ್ ಒಂಜಿ ಕಲ್ಲ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ಟ್ರಫ್ ನ್ ಒಂಜಿ ಡ್ರೈನ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು, ಉಂದು ಒಂಜಿ ಟ್ಯಾಂಕ್ ಗ್ ನೀರ್ ನ್ ಕನಪುಂಡು.ಮೀಪುನ ಜಾಗೆಗ್ ಬರ್ಪಿನವುಲು ಮೆಟ್ಟಿಲುಲು ಉಪ್ಪುಂಡು.<ref>https://whc.unesco.org/en/tentativelists/5892/</ref> ಅಕ್ಟೋಬರ್ ೨೦೧೪ಡ್, ಒಂಜಿ ಆಯತಾಕಾರದ ಮುಟ್ಟ್ ಉಪ್ಪುನ ಗುವೆಲ್ಡ್ ಉತ್ಖನನ ಸುರು ಆಂಡ್, ಉಂದು ೭೩.೪ ಮೀ (೨೪೧ ಅಡಿ) ಉದ್ದ, ೨೯.೩ ಮೀ (೯೬ ಅಡಿ) ಅಗಲ, ಬೊಕ್ಕ ೧೦ ಮೀ (೩೩ ಅಡಿ) ಆಳೊಡು ಇತ್ತ್ಂಡ್, ಉಂದು ಮೊಹೆಂಜೇದಾರೊದ ಮಲ್ಲ ಸ್ನಾನಘಟ್ಟೊಡ್ದ್ ಮೂಜಿ ಪಾಲ್ ಮಲ್ಲವು. ==ಮುದ್ರೆ ಮಲ್ಪುನ== ಧೋಲಾವಿರಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಕೆಲವು ಮುದ್ರೆಲು, ಸ್ಟೇಜ್ III ಗ್ ಸೇರ್ದಿನವು, ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಮಾತ್ರ ಮೂಲು ತೂವೋಲಿ ಅತ್ತಂದೆ, ಒವ್ವೆ ರೀತಿದ ಲಿಪಿ ಇಜ್ಜಿ. ಈ ರೀತಿದ ಮುದ್ರೆಲು ಸಿಂಧು ಮುದ್ರೆ ತಯಾರಿಕೆದ ಪಿರಾಕ್ ದ ಸಂಪ್ರದಾಯೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ==ಬೇತೆ ರಚನೆಲು ಬೊಕ್ಕ ವಸ್ತುಲು== ==ಪೂರ್ವೊದ ದ್ವಾರ== ಆ ಜಾಗೆಡ್ ಒಂಜಿ ಮಲ್ಲ ವೃತ್ತಾಕಾರದ ರಚನೆ ಒಂಜಿ ಸಮಾಧಿ ಅತ್ತಂಡ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ನಂಬುನವು ತಿಕ್ಕ್ಂಡ್, ಆಂಡಲಾ ಅಯಿಟ್ ಅಸ್ಥಿಪಂಜರೊಲು ಅತ್ತಂಡ ಬೇತೆ ಮಾನವ ಅವಶೇಷೊಲು ಇಜ್ಜಿ. ಈ ರಚನೆಡ್ ಒಂಜಿ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ಆಕಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಪತ್ತ್ ರೇಡಿಯಲ್ ಮಣ್ಣ್-ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು ಉಂಡು. ಫಾಲಸ್ ಎರೆಕ್ಟಸ್ ಆಂಡ ತರೆ ಬೊಕ್ಕ ಕಾರ್ ಪಾದೊದ ತಿರ್ತ್ ಕತ್ತರಿಸಾಯಿನ ಒಂಜಿ ಪುರುಷನ ಮೃದುವಾಯಿನ ಮರಳುಕಲ್ಲುದ ಶಿಲ್ಪೊನು ಪೂರ್ವ ದ್ವಾರೊದ ಮಾರ್ಗೊಡು ತೂಯೆರ್. ಮಸ್ತ್ ಶವಸಂಸ್ಕಾರದ ರಚನೆಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ (ಒಂಜಿ ಬುಕ್ಕೊ ಮಾತಾ ಅಸ್ಥಿಪಂಜರೊಲು ಇಜ್ಜಿ), ಅಂಚನೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಟೆರಾಕೋಟಾ ಮುದ್ರೆಲು, ಬಳೆ, ಉಂಗುರೊಲು, ಮಣಿ, ಬೊಕ್ಕ ಕೆತ್ತನೆ. ಡಾನ್ಸಿಂಗ್ ಗರ್ಲ್ ಪಂಡ ಹರಪ್ಪನ್ ನಾಗರಿಕತೆದ ಅತ್ಯಂತ ಪ್ರತಿಷ್ಠಿತ ಕಲಾಕೃತಿಲೆಡ್ ಒಂಜಿ, ಮೊಹೆಂಜೊದಾರೊಡು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಮಾರ್ ಕ್ರಿ.ಪೂ. 2500 ಡ್ದ್ ದುಂಬುದ ಈ ಕಂಚಿದ ಪ್ರತಿಮೆ ಸುಮಾರ್ 10.5 ಸೆಂಟಿಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು ಬೊಕ್ಕ ಒರಿ ಜವನೆರೆನ ನರ್ತಕಿನ ಕ್ರಿಯಾತ್ಮಕ ಶಕ್ತಿ ಬೊಕ್ಕ ಅನುಗ್ರಹೊನು ಸ್ಪಷ್ಟವಾದ್ ಸೆರೆಪತ್ತ್ದ್ಂಡ್. ==ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು== [[File:Cicular houses Dholavira.jpg|thumb|ಧೋಲಾವಿರದ ಗೋಲಾಕಾರದ ಇಲ್ಲುಲು - ಹರಪ್ಪನ್ ಊರು]] ಧೋಲಾವಿರಾಡ್ ಏಳ್ ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲೆನ್ ತೂಯೆರ್, ಅವುಲು ರಡ್ಡ್ ನ್ ವಿವರವಾದ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಅವು ಮಲ್ಲ ಕಲ್ಲ್ ಕತ್ತರಿಸಾಯಿನ ಕೋಣೆಲೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಯೋಜನೆನ್ ಪಡೆಯಿನವು, ಅವು ಮಲ್ಲ ಅರ್ಧಗೋಳಾಕಾರದ ಎತ್ತರದ ಮಣ್ಣ್ ದ ಇಟ್ಟಿಗೆದ ನಿರ್ಮಾಣೊಲು. ಉತ್ಖನನ ಮಲ್ತಿನ ಒಂಜಿ ರಚನೆನ್ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೂಪೊಡು ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕೊಂಜಿಲಾ ಅದೇ ರೀತಿಡ್ ಡಿಸೈನ್ ಮಲ್ತೆರ್, ಆಂಡ ಸ್ಪೋಕ್ಸ್ ಇಜ್ಜಂದಿನ ಚಕ್ರೊದಲೆಕ. ಅವುಲು ಕುಂಬಾರಿಕೆದ ಸಮಾಧಿದ ವಸ್ತುಲು ಇತ್ತ್ಂಡಲಾ, ಒಂಜಿ ಸಮಾಧಿನ್ ಬುಡ್ದು ಬೇತೆ ಒವ್ವೆ ಅಸ್ಥಿಪಂಜರೊಲು ತೋಜಿಜಿ, ಅಲ್ಪ ಒಂಜಿ ಅಸ್ಥಿಪಂಜರ ಬೊಕ್ಕ ಒಂಜಿ ತಾಮ್ರದ ಕನ್ನಡಿ ಉಂಡು. ಒಂಜಿ ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಡ್ ಒಂಜಿ ತಾಮ್ರದ ತಂತಿಗ್ ಕೊಕ್ಕೆಲೆನ್ ಕಟ್ಟ್ ದ್ ದೀತಿನ ಸ್ಟೀಟೈಟ್ ಮಣಿಲೆನ ಕಂಕಣ, ಒಂಜಿ ಬಂಗಾರ್ದ ಬಳೆ, ಬಂಗಾರ್ ಬೊಕ್ಕ ಬೇತೆ ಮಣಿಕುಲುಲಾ ಒಂಜಿ ಅರ್ಧಗೋಳದ ರಚನೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಉತ್ಖನನೊನು ನಡಪಾಯಿನ ಭಾರತದ ಪುರಾತತ್ವ ಸಮೀಕ್ಷೆ, ಈ "ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಲು ಪಿರಾಕ್ದ ಬೌದ್ಧ ಸ್ತೂಪೊಲೆನ್ ನೆನಪು ಮಲ್ಪುಂಡು" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ಪನ್ಪುಂಡು. ಬೊಕ್ಕ "ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ಬೊಕ್ಕ ಅನ್ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೀತಿದ ವಿನ್ಯಾಸ ಶತಪಥ ಬ್ರಾಹ್ಮಣ ಬೊಕ್ಕ ಶುಲ್ಬ ಸೂತ್ರೊಡು ನಮೂದಿಸಾಯಿನ ಸರರತ-ಚಕ್ರ-ಸಿಟಿ ಬೊಕ್ಕ ಸಪ್ರಧಿ-ರತ-ಚಕ್ರ-ಸಿಟಿನ್ ನೆಂಪು ಮಲ್ಪುಂಡು" ==ಉತ್ತರೊದ ದ್ವಾರ== [[File:Dholavira-1.jpg|thumb|ಧೋಲಾವಿರ-1]] ಸಿಂಧು ಲಿಪಿತ ಮಿತ್ತ್ ಕಪ್ಪು ಬಣ್ಣದ ಕುಂಬಾರಿಕೆ, ಚೌಕ ಮುದ್ರೆದ ಮುದ್ರೆಲು, ಸಿಂಧು ಲಿಪಿ ಇಜ್ಜಂದಿನ ಮುದ್ರೆಲು, ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದೊದ ಒಂಜಿ ಮಲ್ಲ ಸೈನ್ ಬೋರ್ಡ್, ಸಿಂಧು ಲಿಪಿತ ಪತ್ತ್ ಅಕ್ಷರೊಲೆನ್ ಸೇರ್ದುಂಡು. ಕಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಒಂಜಿ ಕಳಪೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಕುಲ್ಲುನ ಪುರುಷ ರೂಪೊನುಲಾ ತೂಯೆರ್, ಉಂದು ಹರಪ್ಪಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಉನ್ನತ ಗುಣಮಟ್ಟದ ರಡ್ಡ್ ಕಲ್ಲ್ ದ ಶಿಲ್ಪೊಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಮುಳ್ಳುದ ನೆಲತ ಮಲ್ಲ ಕಪ್ಪು ಜಾರ್ ಲೆನ್ ಲಾ ಈ ಜಾಗೆಡ್ ತೂಯೆರ್. ಒಂಜಿ ಮಲ್ಲ ಕಂಚಿದ ಸುತ್ತಿಗೆ, ಒಂಜಿ ಮಲ್ಲ ಉಳಿ, ಒಂಜಿ ಕಂಚಿನ ಕೈಟ್ ಪತ್ತೊಂದಿಪ್ಪುನ ಕನ್ನಡಿ, ಒಂಜಿ ಬಂಗಾರ್ದ ತಂತಿ, ಬಂಗಾರ್ದ ಕೆಬಿ ಓಲೆ, ತಾಮ್ರದ ಸೆಲ್ಟ್ಸ್ ಬೊಕ್ಕ ಚಿಪ್ಪುದ ಬಳೆ, ಕಲ್ಲ್ ದ ಫಾಲಸ್ ಲೆಕೊದ ಚಿಹ್ನೆಲು, ಸಿಂಧು ಶಾಸನ ಬೊಕ್ಕ ಚಿಹ್ನೆಲೆನ್ ದೀದ್ ಚೌಕ ಮುದ್ರೆಲು, ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಮುದ್ರೆ, ಕುಂಟುದ ಪ್ರಾಣಿಲು, ಪಾಟರ್ ಪೇಂಟೆಡ್ ಪಾಟರ್ಡ್, ಗೊಬ್ಬುಲು, ಡಿಶ್-ಆನ್-ಸ್ಟ್ಯಾಂಡ್, ರಂದ್ರೊಡುಪ್ಪುನ ಜಾರ್ ಲು, ಎಡ್ಡೆ ಆಕಾರೊಡು ಉಪ್ಪುನ ಟೆರಾಕೊಟಾ ಟಂಬ್ಲರ್ ಲು,ಕಡೆಪ್ಪುನ ಕಲ್ಲ್, ಗಾರೆಲು ಇಂಚಿನವುಲಾ ಈ ಜಾಗೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಬೇತೆ ಬೇತೆ ಅಳತೆದ ಕಲ್ಲ್ ದ ತೂಕೊಲೆನ್ ಲಾ ತೂಯೆರ್. ==ಕರಾವಳಿ ಮಾರ್ಗ== ಲೋಥಲ್ ಬೊಕ್ಕ ಧೋಲಾವಿರಾನ್ ಮಕ್ರಾನ್ ಕರಾವಳಿಡ್ ಸುತ್ಕಗನ್ ದೋರ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುನ ಒಂಜಿ ಕರಾವಳಿ ಮಾರ್ಗ ಉಂಡು. ==ಬಾಸೆ ಬೊಕ್ಕ ಲಿಪಿ == ಹರಪ್ಪನೆರ್ ಒಂಜಿ ಅಜ್ಞಾತ ಬಾಸೆನ್ ಪಾತೆರೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಅಕ್ಲೆನ ಲಿಪಿನ್ ಇತ್ತೆಲಾ ಅರ್ಥ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಉಂದೆಟ್ ಸುಮಾರ್ 400 ಮೂಲ ಚಿಹ್ನೆಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ನಂಬಿಕೆ ಉಂಡು, ಉಂದೆಟ್ ಮಸ್ತ್ ಬದಲಾವಣೆಲು ಉಂಡು. ಚಿಹ್ನೆಲು ಪದೊಕುಲೆಗ್ ಬೊಕ್ಕ ಉಚ್ಚಾರೊಲೆಗ್ ನಿಲೆಯಾದಿತ್ತ್ ದ್ ಉಪ್ಪೊಲಿ. ಬರವುದ ದಿಕ್ಕು ಸಾಮಾನ್ಯವಾದ್ ಬಲತ್ತ ಕಡೆಟ್ದ್ ಎಡತ್ತ್ ಗ್ ಇತ್ತ್ಂಡ್. ಹೆಚ್ಚಿನ ಶಾಸನೊಲು ಮುದ್ರೆಲೆಡ್ (ಬಹುತೇಕ ಕಲ್ಲ್ ಡ್ದ್ ಮಲ್ಪುನವು) ಬೊಕ್ಕ ಮುದ್ರೆಲೆಡ್ (ಮಣ್ಣ್ ದ ತುಂಡುಲೆಡ್ ಮುದ್ರೆನ್ ಒತ್ತುದು ಅಯಿತ ಗುರುತುನ್ ಬುಡ್ಪೆರ್) ಉಪ್ಪುಂಡು. ಕೆಲವು ಶಾಸನೊಲು ತಾಮ್ರದ ಫಲಕೊಲೆಡ್, ಕಂಚಿದ ಉಪಕರಣೊಲೆಡ್, ಬೊಕ್ಕ ಟೆರಾಕೊಟಾ, ಕಲ್ಲ್ ಬೊಕ್ಕ ಫೇಯೆನ್ಸ್ ಡ್ ಮಲ್ತಿನ ಎಲ್ಯ ವಸ್ತುಲೆಡ್ಲಾ ತೋಜುಂಡು. ಈ ಮುದ್ರೆಲೆನ್ ವ್ಯಾಪಾರೊಡು ಬೊಕ್ಕ ಅಧಿಕೃತ ಆಡಳಿತ ಕೆಲಸೊಗುಲಾ ಬಳಕೆ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಮೊಹೆಂಜೊದಾರೊ ಬೊಕ್ಕ ಬೇತೆ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ಜಾಗೆಲೆಡ್ ಮಸ್ತ್ ಕೆತ್ತನೆದ ಸಾಮಗ್ರಿಲೆನ್ ತೂಯೆರ್.<ref>https://web.archive.org/web/20110903114242/http://asi.nic.in/asi_exca_2007_dholavira.asp</ref> ==ಸೈನ್ ಬೋರ್ಡ್== [[File:The 'Ten Indus Scripts' discovered near the northern gateway of the Dholavira citadel.jpg|thumb|Ten Indus characters from the northern gate of Dholavira, dubbed the Dholavira Signboard, one of the longest known sequences of Indus characters]] ಧೋಲಾವಿರೊಡು ಮಸ್ತ್ ಮಹತ್ವದ ಅನ್ವೇಷಣೆಲು ನಗರೊದ ಉತ್ತರ ದ್ವಾರೊದ ಒಂಜಿ ಸೈಡ್ ರೂಮ್ ಡ್ ನಡತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಧೋಲಾವಿರ ಸೈನ್ಬೋರ್ಡ್ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ಹರಪ್ಪೆರ್ ಖನಿಜ ಜಿಪ್ಸಮ್ ದ ತುಂಡುಲೆನ್ ಒಂಜಿ ಮಲ್ಲ ಮರತ್ತ ಫಲಕೊಡು ಪತ್ತ್ ಮಲ್ಲ ಚಿಹ್ನೆಲೆನ್ ಅತ್ತಂಡ ಅಕ್ಷರೊಲೆನ್ ರೂಪಿಸೆರೆ ಜೋಡಾದ್ ದೀತೆರ್. ಒಂಜಿ ಸರ್ತಿ, ಫಲಕ ಅಯಿತ ಮೋನೆಡ್ ಫ್ಲಾಟ್ ಆದ್ ಬೂರುಂಡು. ಮರ ಕುಸಿತ್ಂಡ್, ಆಂಡ ಅಕ್ಷರೊಲೆನ ವ್ಯವಸ್ಥೆ ಒರಿದ್ಂಡ್. ಸೈನ್ ಬೋರ್ಡ್ ದ ಅಕ್ಷರೊಲು ಸುತ್ತಮುತ್ತದ ಗೋಡೆಲೆಡ್ ಬಳಕೆ ಆಯಿನ ಮಲ್ಲ ಇಟ್ಟಿಗೆಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಪ್ರತಿಯೊಂಜಿ ಚಿಹ್ನೆ ಸುಮಾರ್ 37 ಸೆಂ (15 ಇಂಚ್) ಎತ್ತರೊಡು ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಬೋರ್ಡ್ ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದ ಉಪ್ಪುಂಡು. ಈ ಶಾಸನ ಸಿಂಧು ಲಿಪಿಟ್ ಅತಿ ಉದ್ದೊದ ಶಾಸನೊಲೆಡ್ ಒಂಜಿ, ಒಂಜಿ ಚಿಹ್ನೆ ನಾಲ್ ಸರ್ತಿ ತೋಜುಂಡು, ಬೊಕ್ಕ ಅಯಿತ ಮಲ್ಲ ಗಾತ್ರ ಬೊಕ್ಕ ಸಾರ್ವಜನಿಕ ಸ್ವಭಾವೊ ಸಿಂಧು ಲಿಪಿ ಪೂರ್ತಿ ಸಾಕ್ಷರತೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಈ ಜಾಗೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ಲ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಒಂಜಿ ನಾಲ್ ಚಿಹ್ನೆದ ಶಾಸನೊನುಲಾ ತೂವೊಲಿ, ಉಂದು ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಇಂಚಿನ ಶಾಸನೊಲೆಡ್ ಸುರುತವು ಆದುಂಡು ಪನ್ಪೆರ್. [[ವರ್ಗ:Event:Heritage Across Borders]] 7n7bnmj128drm4zcczdwtb3h7lhefyl 366200 366199 2026-08-21T10:24:21Z Kavitha G. Kana 1040 366200 wikitext text/x-wiki [[File:DHOLAVIRA SITE (24).jpg|thumb|ಧೋಲಾವಿರ ಜಾಗೆ (24)]] ಧೋಲಾವಿರ ಪಂಡ ಪಶ್ಚಿಮ ಭಾರತೊದ [[ಗುಜರಾತ್]] ರಾಜ್ಯೊದ [[ಕಚ್]] ಜಿಲ್ಲೆದ ಭಾಚೌ ತಾಲೂಕುದ ಖಾದಿರ್ಬೆಟ್ ಡ್ ಉಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಜಾಗೆ, ಉಂದೆತ ಪುದರ್ ದಕ್ಷಿಣೊಡು 1 ಕಿಲೋಮೀಟರ್ (0.62 ಮೈಲ್) ದೂರೊಡು ಉಪ್ಪುನ ಇತ್ತೆದ ಗ್ರಾಮೊದ ಪುದರ್ ಡ್ ಉಂಡು. ಈ ಊರು ರಾಧನ್ಪುರ್ ಡ್ದ್ 165 ಕಿ.ಮೀ (103 ಮೈಲ್) ದೂರೊಡು ಉಂಡು. ಸ್ಥಳೀಯವಾದ್ ಕೋಟದ ಟಿಂಬಾ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಈ ಜಾಗೆಡ್ ಪ್ರಾಚೀನ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ನಗರೊದ ಅವಶೇಷೊಲು ಉಂಡು. ಭೂಕಂಪೊಲು ಧೋಲಾವಿರ ಪನ್ಪಿನ ಈ ಜಾಗೆಗ್ ಸುಮಾರ್ ಸರ್ತಿ ಅಯಿತ ಕೆಟ್ಟ ಪರಿಣಾಮೊನ್ ಬೀರ್ದ್ಂಡ್ .<ref>https://www.thehindu.com/society/ruins-on-the-tropic-of-cancer/article19284214.ece</ref> ==ಜಾಗೆ== ಧೋಲಾವಿರ ಪನ್ಪಿನ ಜಾಗೆ ಉಷ್ಣವಲಯೊಡು(Tropic of cancer) ಉಂಡು. ಉಂದು ಐನ್ ಮಲ್ಲ ಹರಪ್ಪನ್ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಬೊಕ್ಕ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆಗ್ ಸೇರ್ದಿನ ಭಾರತೊದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಪಿರಾಕ್ದ ತಾಣ. ಉಂದು ಕಚ್ ಮರುಭೂಮಿದ ವನ್ಯಜೀವಿ ಅಭಯಾರಣ್ಯೊಡು ಕಚ್ ಗ್ರೇಟ್ ರನ್ ಡ್ ಖಾದಿರ್ ಬೆಟ್ ದ್ವೀಪೊಡು ಉಂಡು. ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಚತುರ್ಭುಜಾಕೃತಿದ ನಗರ ಉತ್ತರೊಡು ಮನ್ಸಾರ್ ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹಾರ್ ಪನ್ಪಿನ ಸುದೆತ್ತ ನಡುಟು ಉಂಡು. ಕ್ರಿ.ಪೂ ೧೪೫೦; ಆಂಡಲಾ, ಇತ್ತೆದ ಸಂಶೋಧನೆ ಸುಮಾರ್ ಕ್ರಿ.ಪೂ.೩೫೦೦ಡ್ (ಹರಪ್ಪನ್ ಪೂರ್ವ) ಆಕ್ರಮಣದ ಸುರುತ ಬೊಕ್ಕ ಕ್ರಿ.ಪೂ ೧೮೦೦ (ಹರಪ್ಪನ್ ಕಾಲೊದ ಸುರುತ ಭಾಗ) ಮುಟ್ಟ ನಿರಂತರತೆನ್ ಸೂಚಿಸವುಂಡು ==ಅನ್ವೇಷಣೆ== ಈ ಜಾಗೆನ್ ಸುರುಕು ಧೋಲಾವಿರ ಗ್ರಾಮದ ನಿವಾಸಿ ಶಂಭೂದನ್ ಗಾಧವಿ 1960 ದ ಸುರುತ ಭಾಗೊಡು ಕಂಡು ಕನತ್ದ್, ಸರ್ಕಾರದ ಗಮನೊನು ಈ ಜಾಗೆಗ್ ಕನರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. ಈ ಜಾಗೆನ್ 1967 ಅತ್ತ್ಂಡ 1968ಡ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ದ ಜೆ.ಪಿ.ಜೋಶಿ "ಅಧಿಕೃತವಾದ್" ಬೊಳ್ಪುಗು ಕನತೆರ್, ಬೊಕ್ಕ ಉಂದು ಎಣ್ಮ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಐನ್ ನೇ ಮಲ್ಲ ಜಾಗೆ. ಉಂದು ೧೯೯೦ಡ್ದ್ ಎಎಸ್ಐಡ್ದ್ ಉತ್ಖನನೊಡು ಉಂಡು, ಉಂದು "ಧೋಲಾವಿರ ಸಿಂಧು ಕಣಿವೆದ ನಾಗರಿಕತೆದ ಅರ್ಥ ಮಲ್ಪುನಗ ನಿಜವಾದ್ ಲಾ ಪೊಸ ಆಯಾಮೊಲೆನ್ ಸೇರ್ಸಾದ್ಂಡ್" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ತೋಜಾದ್ಂಡ್. ಧೋಲಾವಿರಾನ್ ಕಚ್ ದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಮೆಗಾಲಿಥಿಕ್(ಶಿಲಾಯುಗ) ತಾಣೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಂಡುಂಡು, ಆಂಡ ಕಚ್ಚ್ ಡ್ ತೆರಿನ ಒಂಜೇ ಒಂಜಿ ಮೆಗಾಲಿಥಿಕ್ ತಾಣ ಧನೆತಿ ಗ್ರಾಮದ ಸಮೀಪೊಡು ಉಂಡು. ಇತ್ತೆ ಮುಟ್ಟ ತೋಜುದು ಬತ್ತಿನ ಬೇತೆ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ತಾಣೊಲು ಹರಪ್ಪಾ, [[ಮೊಹೆಂಜೊದಾರೊ,]] ಗಣೇರಿವಾಳ, ರಾಖಿಗಡಿ, ಕಾಳಿಬಂಗನ್, ರೂಪನಗರ ಬೊಕ್ಕ ಲೋಥಲ್. ಜುಲೈ ೨೭, ೨೦೨೧ಡ್ ಧೋಲಾವಿರ: ಒಂಜಿ ಹರಪ್ಪನ್ ನಗರ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಪುದರ್ ದೀಯೆರ್ ==ಉತ್ಖನನೊಲು== ಬಿಷ್ಟ್ ನ ನಿರ್ದೇಶನೊಡು ಎಎಸ್ಐ 1989ಡ್ ಉತ್ಖನನ ಸುರು ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ 1990 ಬೊಕ್ಕ 2005 ನಡುಟು 13 ಕ್ಷೇತ್ರ ಉತ್ಖನನೊಲು ನಡತ್ಂಡ್. ಉತ್ಖನನ ನಗರ ಯೋಜನೆ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಬೊಲ್ಪುಗು ಕನೆತೆರ್, ಬೊಕ್ಕ ಪ್ರಾಣಿಲೆನ ಮೂಳೆಲು, ಚಿನ್ನ, ಬೊಳ್ಳಿ, ಟೆರ್ನಾಕೊಟರ್, ಬೊಕ್ಕ ಕಂಚಿದಂಚಿನ ಮಸ್ತ್ ಸಂಖ್ಯೆಡ್ ಪ್ರಾಚೀನ ವಸ್ತುಲೆನ್ ಕಂಡು ಕನತ್ಂಡ್. ದಕ್ಷಿಣ ಗುಜರಾತ್, ಸಿಂಧ್ ಬೊಕ್ಕ ಪಂಜಾಬ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿ ಏಷ್ಯಾದ ವಸತಿ ಸ್ಥಾನೊಲೆನ ನಡುಟು ಧೋಲಾವಿರ ಒಂಜಿ ಪ್ರಮುಖ ವ್ಯಾಪಾರ ಕೇಂದ್ರ ಆದಿತ್ತ್ಂಡ್ ಪಂದ್ ಪುರಾತತ್ತ್ವಜ್ಞೆರ್ ನಂಬುವೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== [[ಲೋಥಲ್]] ಬಂದರ್-ನಗರೊಡ್ದ್ ಪಿರಾಕ್ ದವು ಪನ್ಪಿನ ಅಂದಾಜಿಡ್, ಧೋಲಾವಿರಾ ನಗರೊಗು ಆಯತಾಕಾರೊದ ಆಕಾರೊ ಬೊಕ್ಕ ಸಂಘಟನೆ ಉಂಡು, ಬೊಕ್ಕ ಉಂದು 22 ಹೆಕ್ಟೇರ್ (54 ಎಕ್ರೆ) ಡ್ ವ್ಯಾಪಿಸಾಯಿನವು. ಈ ಪ್ರದೇಶೊದ ಉದ್ದ 771.1 ಮೀ (2,530 ಅಡಿ), ಬೊಕ್ಕ ಅಗಲ 616.85 ಮೀ (2,023.8 ಅಡಿ) ಉಂಡು. ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊದಾರೊದಲೆಕ ಅತ್ತ್, ಈ ನಗರೊನು ಮೂಜಿ ವಿಭಾಗೊಲೆನ್ ಸೇರ್ದಂಚಿನ ಒಂಜಿ ದುಂಬೇ ಇತ್ತಿನ ಜ್ಯಾಮಿತೀಯ ಯೋಜನೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ - ಮಿತ್ತ ಕೋಟೆ,ನಡುತ್ತ ಊರು, ಬೊಕ್ಕ ತಿರ್ತದ ಊರು. ಅಕ್ರೊಪೊಲಿಸ್ ಬೊಕ್ಕ ನಡುತ್ತ ಊರು ಅಕ್ಲೆನ ಸ್ವಂತ ರಕ್ಷಣಾ ಕೆಲಸ, ದ್ವಾರೊಲು, ನಿರ್ಮಾಣ ಆಯಿನ ಪ್ರದೇಶೊಲು,ಸಾದಿ ವ್ಯವಸ್ಥೆ,ಗುವೆಲ್ ಬೊಕ್ಕ ಮಲ್ಲ ತೆರೆದಿಪ್ಪುನ ಜಾಗೊಲೆನ್ ಸಜ್ಜು ಮಲ್ತೆರ್.ಉಂದೆಟ್ ನೈಋತ್ಯ ವಲಯೊದ ಪ್ರಮುಖ ಭಾಗೊನು ಸ್ವಾಧೀನ ಮಲ್ಪುಂಡು. ಎತ್ತರದ "ಕೋಟೆ" ನಿಲೆಲೆನ್ ಡಬಲ್ ರಾಂಪಾರ್ಟ್ ಲೆಡ್ ರಕ್ಷಣೆ ಮಲ್ಪುಂಡು. ಉಂದೆತ ಬರಿಟ್ 'ಬೈಲಿ' ಪನ್ಪಿನ ಜಾಗೆ ಉಂಡು ಅಲ್ಪ ಪ್ರಮುಖ ಅಧಿಕಾರಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಸಾಮಾನ್ಯ ಕೋಟೆಲೆನ ಉಳಯಿ ಉಪ್ಪುನ ನಗರ ಪ್ರದೇಸೊ 48 ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಉಂಡು. ಕೋಟೆದ ವಸಾಹತುಗು ಅವಿಭಾಜ್ಯವಾದ್ ಉಪ್ಪುನ ವಿಸ್ತಾರವಾದ್ ರಚನೆ-ಬೇರಿಂಗ್ ಪ್ರದೇಶೊಲು ಉಂಡು. ಗೋಡೆಲೆನ ಮಿತ್ತ್, ಬೇತೆ ಒಂಜಿ ವಸಾಹತು ತೋಜಿದ್ಬತ್ತ್ಂಡ್. ನಗರೊದ ಅತ್ಯಂತ ಗಮನಾರ್ಹ ಲಕ್ಷಣ ಪಂಡ ಅಯಿತ ಮಾತಾ ಕಟ್ಟಡೊಲು, ಕನಿಷ್ಠ ಅಯಿತ ಇತ್ತೆದ ಸಂರಕ್ಷಣೆದ ಸ್ಥಿತಿಡ್, ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್, ಆಂಡ ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊ-ದಾರೊ ಸೇರ್ದ್ ಬೇತೆ ಹರಪ್ಪನ್ ತಾಣೊಲು ಮಾತಲಾ ಇಟ್ಟಿಗೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಧೋಲಾವಿರಾ ರಡ್ಡ್ ತುಫಾನ್ ನೀರ್ದ ಕಾಲುವೆಲೆಡ್ದ್ ಸುತ್ತುವರಿದ್ ಉಂಡು; ಉತ್ತರೊಡು ಮನ್ಸರ್, ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹರ್. ಊರುದ ಚೌಕೊಡು, ನೆಲತ ಮಿತ್ತ್ ಎತ್ತರೊಡು ಒಂಜಿ ಜಾಗೆ ಉಂಡು, ಅವೆನ್ "ಕೋಟೆ" ಪನ್ಪೆರ್.<ref>https://web.archive.org/web/20170812062930/https://archaeologynewsnetwork.blogspot.com/2010/06/rise-and-fall-of-harappan-city.html</ref> ==ಜಲಾಶಯೊಲು== ಎಎಸ್ಐ ದ ಜಂಟಿ ಮಹಾನಿರ್ದೇಶಕೆರಾದ್ ನಿವೃತ್ತರಾಯಿನ ಬಿಷ್ತ್ ಪನ್ಪೆರ್, "ನೀರ್ ದ ಸಂರಕ್ಷಣೆ, ಕೊಯ್ಲು ಬೊಕ್ಕ ಸಂಗ್ರಹೊಗು ಅಭಿವೃದ್ಧಿ ಮಲ್ತಿನ ಧೋಲಾವಿರದ ಹರಪ್ಪನೆರೆನ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಸ್ಥೆ ಅಕ್ಲೆನ ಸುಧಾರಿತ ಹೈಡ್ರಾಲಿಕ್ ಎಂಜಿನಿಯರಿಂಗ್ ದ ಬಗ್ಗೆ ಪಾತೆರುಂಡು, ಕ್ರಿ.ಪೂ. ಮೂಜಿನೆ ಸಹಸ್ರಮಾನೊಡು ತಂತ್ರಜ್ಞಾನದ ಸ್ಥಿತಿನ್ ತೂಂಡ ಡೊಲಾವಿರಾದ ಸೋಫ್ಯಾಸ್ಟಿಕ್ ನೀರ್ ದ ಅನನ್ಯ ಲಕ್ಷಣೊಲು ಒಂಜಿ." ಚಾನೆಲ್ ಬೊಕ್ಕ ಜಲಾಶಯೊಲೆನ ಸಂರಕ್ಷಣೆದ ವ್ಯವಸ್ಥೆ, ಜಗತ್ತ್ಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಸುರುತ ವ್ಯವಸ್ಥೆ, ಉಂದು ಪೂರ್ತಿ ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ನಗರೊಡು ಮಲ್ಲ ಮಲ್ಲ ಜಲಾಶಯೊಲು ಇತ್ತ್ಂಡ್, ಅವುಲು ಮೂಜಿ ಜಲಾಶಯೊಲು ಬಹಿರಂಗ ಆತ್ಂಡ್. ಅವು ರಡ್ಡ್ ಗಿಟ್ಟದ ಕೆರೆಕುಲೆಡ್ದ್ ಬೇತೆ ಬೇತೆ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಬರ್ಸೊಡ್ದ್ ಬತ್ತಿನ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಆಪುಂಡು. ಉಂದು ಸ್ಪಷ್ಟವಾದ್ ಕಚ್ ದ ಮರುಭೂಮಿದ ಹವಾಮಾನ ಬೊಕ್ಕ ಪರಿಸ್ಥಿತಿಲೆಗ್ ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ಬತ್ತ್ಂಡ್, ಅಲ್ಪ ಕೆಲವು ವರ್ಷೊಲು ಬರ್ಸ ಬರ್ಪುಜಿ. ಈ ಜಾಗೆದ ಸಮೀಪೊಡು ಉತ್ತರ-ದಕ್ಷಿಣ ದಿಕ್ಕ್ ಡ್ ಪೋಪಿನ ಒಂಜಿ ಋತುಕಾಲಿಕ ನೀರ್ ನ್ ನೀರ್ ಸಂಗ್ರಹ ಮಲ್ಪರೆ ಕೆಲವು ಕಡೆಟ್ ಅಣೆಕಟ್ಟ್ ಮಲ್ತೆರ್. ೧೯೯೮ಡ್, ಈ ಜಾಗೆಡ್ ಬೇತೆ ಒಂಜಿ ಜಲಾಶಯೊನು ಕಂಡು ಕನತ್ ದೆರ್.<ref>https://www.cntraveller.in/magazine-story/india-via-gujarat/</ref> ಧೋಲಾವಿರಾದ ನಿವಾಸಿಲು ಮೂಜಿನೆ ಹಂತೊಡು ಬೇತೆ ಬೇತೆ ಗಾತ್ರೊದ ಪದಿನಾಜಿ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ಜಲಾಶಯೊಲೆನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಅವುಲು ಕೆಲವು ಮಲ್ಲ ವಸಾಹತುದ ತಿರ್ತ್ ನೆಲತ ಇಳಿಜಾರು ಪ್ರದೇಸೊದ ಲಾಭ ದೆತೊನುವೆರ್ ಅವುಲು ಗುವೆಲ್ದಂಚಿನ ರಚನೆ ಮಲ್ಪುವೆರ್.. ಬೇತೆ ಜಲಾಶಯೊಲೆನ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಕೆಲವು ಕಲ್ಲ್ಡ್ ಕಟ್ಟ್ದೆರ್ . ಇತ್ತೆದ ಕೆಲಸೊಡು ರಡ್ಡ್ ಮಲ್ಲ ಜಲಾಶಯೊಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್, ಒಂಜಿ ಕೋಟೆದ ಪೂರ್ವೊಡ್ ಬೊಕ್ಕೊಂಜಿ ಅಯಿತ ತೆಂಕ್ ದಿಕ್ಕ್ ಗ್ ಅನೆಕ್ಸ್ ದ ಸಮೀಪೊಡು. ಜಲಾಶಯೊಲು ಕಲ್ಲ್ ಡ್ದ್ ಲಂಬವಾದ್ ಕಡ್ತ್ ದ್ ಉಪ್ಪುಂಡು, ಬೊಕ್ಕ ಅವು ಸುಮಾರ್ 7 ಮೀ (23 ಅಡಿ) ಆಳೊಡು ಬೊಕ್ಕ 79 ಮೀ (259 ಅಡಿ) ಉದ್ದೊಡು ಉಪ್ಪುಂಡು. ಅವು ನಗರೊನು ಸುತ್ತುವರಿದ್ ಉಪ್ಪುಂಡು, ಆಂಡ ಕೋಟೆ ಬೊಕ್ಕ ಮೀಪುನ ಸ್ನಾನಘಟ್ಟಡ್ ಕೇಂದ್ರೊಡು ಎತ್ತರದ ನೆಲಟ್ ಉಪ್ಪುಂಡು. ಒಂಜಿ ಮಲ್ಲ ಗೂವೆಲ್ ಲಾ ಉಂಡು, ಉಂದೆಟ್ ಒಂಜಿ ಕಲ್ಲ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ಟ್ರಫ್ ನ್ ಒಂಜಿ ಡ್ರೈನ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು, ಉಂದು ಒಂಜಿ ಟ್ಯಾಂಕ್ ಗ್ ನೀರ್ ನ್ ಕನಪುಂಡು.ಮೀಪುನ ಜಾಗೆಗ್ ಬರ್ಪಿನವುಲು ಮೆಟ್ಟಿಲುಲು ಉಪ್ಪುಂಡು.<ref>https://whc.unesco.org/en/tentativelists/5892/</ref> ಅಕ್ಟೋಬರ್ ೨೦೧೪ಡ್, ಒಂಜಿ ಆಯತಾಕಾರದ ಮುಟ್ಟ್ ಉಪ್ಪುನ ಗುವೆಲ್ಡ್ ಉತ್ಖನನ ಸುರು ಆಂಡ್, ಉಂದು ೭೩.೪ ಮೀ (೨೪೧ ಅಡಿ) ಉದ್ದ, ೨೯.೩ ಮೀ (೯೬ ಅಡಿ) ಅಗಲ, ಬೊಕ್ಕ ೧೦ ಮೀ (೩೩ ಅಡಿ) ಆಳೊಡು ಇತ್ತ್ಂಡ್, ಉಂದು ಮೊಹೆಂಜೇದಾರೊದ ಮಲ್ಲ ಸ್ನಾನಘಟ್ಟೊಡ್ದ್ ಮೂಜಿ ಪಾಲ್ ಮಲ್ಲವು. ==ಮುದ್ರೆ ಮಲ್ಪುನ== ಧೋಲಾವಿರಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಕೆಲವು ಮುದ್ರೆಲು, ಸ್ಟೇಜ್ III ಗ್ ಸೇರ್ದಿನವು, ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಮಾತ್ರ ಮೂಲು ತೂವೋಲಿ ಅತ್ತಂದೆ, ಒವ್ವೆ ರೀತಿದ ಲಿಪಿ ಇಜ್ಜಿ. ಈ ರೀತಿದ ಮುದ್ರೆಲು ಸಿಂಧು ಮುದ್ರೆ ತಯಾರಿಕೆದ ಪಿರಾಕ್ ದ ಸಂಪ್ರದಾಯೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ==ಬೇತೆ ರಚನೆಲು ಬೊಕ್ಕ ವಸ್ತುಲು== ==ಪೂರ್ವೊದ ದ್ವಾರ== ಆ ಜಾಗೆಡ್ ಒಂಜಿ ಮಲ್ಲ ವೃತ್ತಾಕಾರದ ರಚನೆ ಒಂಜಿ ಸಮಾಧಿ ಅತ್ತಂಡ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ನಂಬುನವು ತಿಕ್ಕ್ಂಡ್, ಆಂಡಲಾ ಅಯಿಟ್ ಅಸ್ಥಿಪಂಜರೊಲು ಅತ್ತಂಡ ಬೇತೆ ಮಾನವ ಅವಶೇಷೊಲು ಇಜ್ಜಿ. ಈ ರಚನೆಡ್ ಒಂಜಿ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ಆಕಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಪತ್ತ್ ರೇಡಿಯಲ್ ಮಣ್ಣ್-ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು ಉಂಡು. ಫಾಲಸ್ ಎರೆಕ್ಟಸ್ ಆಂಡ ತರೆ ಬೊಕ್ಕ ಕಾರ್ ಪಾದೊದ ತಿರ್ತ್ ಕತ್ತರಿಸಾಯಿನ ಒಂಜಿ ಪುರುಷನ ಮೃದುವಾಯಿನ ಮರಳುಕಲ್ಲುದ ಶಿಲ್ಪೊನು ಪೂರ್ವ ದ್ವಾರೊದ ಮಾರ್ಗೊಡು ತೂಯೆರ್. ಮಸ್ತ್ ಶವಸಂಸ್ಕಾರದ ರಚನೆಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ (ಒಂಜಿ ಬುಕ್ಕೊ ಮಾತಾ ಅಸ್ಥಿಪಂಜರೊಲು ಇಜ್ಜಿ), ಅಂಚನೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಟೆರಾಕೋಟಾ ಮುದ್ರೆಲು, ಬಳೆ, ಉಂಗುರೊಲು, ಮಣಿ, ಬೊಕ್ಕ ಕೆತ್ತನೆ. ಡಾನ್ಸಿಂಗ್ ಗರ್ಲ್ ಪಂಡ ಹರಪ್ಪನ್ ನಾಗರಿಕತೆದ ಅತ್ಯಂತ ಪ್ರತಿಷ್ಠಿತ ಕಲಾಕೃತಿಲೆಡ್ ಒಂಜಿ, ಮೊಹೆಂಜೊದಾರೊಡು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಮಾರ್ ಕ್ರಿ.ಪೂ. 2500 ಡ್ದ್ ದುಂಬುದ ಈ ಕಂಚಿದ ಪ್ರತಿಮೆ ಸುಮಾರ್ 10.5 ಸೆಂಟಿಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು ಬೊಕ್ಕ ಒರಿ ಜವನೆರೆನ ನರ್ತಕಿನ ಕ್ರಿಯಾತ್ಮಕ ಶಕ್ತಿ ಬೊಕ್ಕ ಅನುಗ್ರಹೊನು ಸ್ಪಷ್ಟವಾದ್ ಸೆರೆಪತ್ತ್ದ್ಂಡ್. ==ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು== [[File:Cicular houses Dholavira.jpg|thumb|ಧೋಲಾವಿರದ ಗೋಲಾಕಾರದ ಇಲ್ಲುಲು - ಹರಪ್ಪನ್ ಊರು]] ಧೋಲಾವಿರಾಡ್ ಏಳ್ ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲೆನ್ ತೂಯೆರ್, ಅವುಲು ರಡ್ಡ್ ನ್ ವಿವರವಾದ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಅವು ಮಲ್ಲ ಕಲ್ಲ್ ಕತ್ತರಿಸಾಯಿನ ಕೋಣೆಲೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಯೋಜನೆನ್ ಪಡೆಯಿನವು, ಅವು ಮಲ್ಲ ಅರ್ಧಗೋಳಾಕಾರದ ಎತ್ತರದ ಮಣ್ಣ್ ದ ಇಟ್ಟಿಗೆದ ನಿರ್ಮಾಣೊಲು. ಉತ್ಖನನ ಮಲ್ತಿನ ಒಂಜಿ ರಚನೆನ್ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೂಪೊಡು ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕೊಂಜಿಲಾ ಅದೇ ರೀತಿಡ್ ಡಿಸೈನ್ ಮಲ್ತೆರ್, ಆಂಡ ಸ್ಪೋಕ್ಸ್ ಇಜ್ಜಂದಿನ ಚಕ್ರೊದಲೆಕ. ಅವುಲು ಕುಂಬಾರಿಕೆದ ಸಮಾಧಿದ ವಸ್ತುಲು ಇತ್ತ್ಂಡಲಾ, ಒಂಜಿ ಸಮಾಧಿನ್ ಬುಡ್ದು ಬೇತೆ ಒವ್ವೆ ಅಸ್ಥಿಪಂಜರೊಲು ತೋಜಿಜಿ, ಅಲ್ಪ ಒಂಜಿ ಅಸ್ಥಿಪಂಜರ ಬೊಕ್ಕ ಒಂಜಿ ತಾಮ್ರದ ಕನ್ನಡಿ ಉಂಡು. ಒಂಜಿ ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಡ್ ಒಂಜಿ ತಾಮ್ರದ ತಂತಿಗ್ ಕೊಕ್ಕೆಲೆನ್ ಕಟ್ಟ್ ದ್ ದೀತಿನ ಸ್ಟೀಟೈಟ್ ಮಣಿಲೆನ ಕಂಕಣ, ಒಂಜಿ ಬಂಗಾರ್ದ ಬಳೆ, ಬಂಗಾರ್ ಬೊಕ್ಕ ಬೇತೆ ಮಣಿಕುಲುಲಾ ಒಂಜಿ ಅರ್ಧಗೋಳದ ರಚನೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಉತ್ಖನನೊನು ನಡಪಾಯಿನ ಭಾರತದ ಪುರಾತತ್ವ ಸಮೀಕ್ಷೆ, ಈ "ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಲು ಪಿರಾಕ್ದ ಬೌದ್ಧ ಸ್ತೂಪೊಲೆನ್ ನೆನಪು ಮಲ್ಪುಂಡು" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ಪನ್ಪುಂಡು. ಬೊಕ್ಕ "ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ಬೊಕ್ಕ ಅನ್ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೀತಿದ ವಿನ್ಯಾಸ ಶತಪಥ ಬ್ರಾಹ್ಮಣ ಬೊಕ್ಕ ಶುಲ್ಬ ಸೂತ್ರೊಡು ನಮೂದಿಸಾಯಿನ ಸರರತ-ಚಕ್ರ-ಸಿಟಿ ಬೊಕ್ಕ ಸಪ್ರಧಿ-ರತ-ಚಕ್ರ-ಸಿಟಿನ್ ನೆಂಪು ಮಲ್ಪುಂಡು" ==ಉತ್ತರೊದ ದ್ವಾರ== [[File:Dholavira-1.jpg|thumb|ಧೋಲಾವಿರ-1]] ಸಿಂಧು ಲಿಪಿತ ಮಿತ್ತ್ ಕಪ್ಪು ಬಣ್ಣದ ಕುಂಬಾರಿಕೆ, ಚೌಕ ಮುದ್ರೆದ ಮುದ್ರೆಲು, ಸಿಂಧು ಲಿಪಿ ಇಜ್ಜಂದಿನ ಮುದ್ರೆಲು, ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದೊದ ಒಂಜಿ ಮಲ್ಲ ಸೈನ್ ಬೋರ್ಡ್, ಸಿಂಧು ಲಿಪಿತ ಪತ್ತ್ ಅಕ್ಷರೊಲೆನ್ ಸೇರ್ದುಂಡು. ಕಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಒಂಜಿ ಕಳಪೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಕುಲ್ಲುನ ಪುರುಷ ರೂಪೊನುಲಾ ತೂಯೆರ್, ಉಂದು ಹರಪ್ಪಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಉನ್ನತ ಗುಣಮಟ್ಟದ ರಡ್ಡ್ ಕಲ್ಲ್ ದ ಶಿಲ್ಪೊಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಮುಳ್ಳುದ ನೆಲತ ಮಲ್ಲ ಕಪ್ಪು ಜಾರ್ ಲೆನ್ ಲಾ ಈ ಜಾಗೆಡ್ ತೂಯೆರ್. ಒಂಜಿ ಮಲ್ಲ ಕಂಚಿದ ಸುತ್ತಿಗೆ, ಒಂಜಿ ಮಲ್ಲ ಉಳಿ, ಒಂಜಿ ಕಂಚಿನ ಕೈಟ್ ಪತ್ತೊಂದಿಪ್ಪುನ ಕನ್ನಡಿ, ಒಂಜಿ ಬಂಗಾರ್ದ ತಂತಿ, ಬಂಗಾರ್ದ ಕೆಬಿ ಓಲೆ, ತಾಮ್ರದ ಸೆಲ್ಟ್ಸ್ ಬೊಕ್ಕ ಚಿಪ್ಪುದ ಬಳೆ, ಕಲ್ಲ್ ದ ಫಾಲಸ್ ಲೆಕೊದ ಚಿಹ್ನೆಲು, ಸಿಂಧು ಶಾಸನ ಬೊಕ್ಕ ಚಿಹ್ನೆಲೆನ್ ದೀದ್ ಚೌಕ ಮುದ್ರೆಲು, ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಮುದ್ರೆ, ಕುಂಟುದ ಪ್ರಾಣಿಲು, ಪಾಟರ್ ಪೇಂಟೆಡ್ ಪಾಟರ್ಡ್, ಗೊಬ್ಬುಲು, ಡಿಶ್-ಆನ್-ಸ್ಟ್ಯಾಂಡ್, ರಂದ್ರೊಡುಪ್ಪುನ ಜಾರ್ ಲು, ಎಡ್ಡೆ ಆಕಾರೊಡು ಉಪ್ಪುನ ಟೆರಾಕೊಟಾ ಟಂಬ್ಲರ್ ಲು,ಕಡೆಪ್ಪುನ ಕಲ್ಲ್, ಗಾರೆಲು ಇಂಚಿನವುಲಾ ಈ ಜಾಗೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಬೇತೆ ಬೇತೆ ಅಳತೆದ ಕಲ್ಲ್ ದ ತೂಕೊಲೆನ್ ಲಾ ತೂಯೆರ್. ==ಕರಾವಳಿ ಮಾರ್ಗ== ಲೋಥಲ್ ಬೊಕ್ಕ ಧೋಲಾವಿರಾನ್ ಮಕ್ರಾನ್ ಕರಾವಳಿಡ್ ಸುತ್ಕಗನ್ ದೋರ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುನ ಒಂಜಿ ಕರಾವಳಿ ಮಾರ್ಗ ಉಂಡು. ==ಬಾಸೆ ಬೊಕ್ಕ ಲಿಪಿ == ಹರಪ್ಪನೆರ್ ಒಂಜಿ ಅಜ್ಞಾತ ಬಾಸೆನ್ ಪಾತೆರೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಅಕ್ಲೆನ ಲಿಪಿನ್ ಇತ್ತೆಲಾ ಅರ್ಥ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಉಂದೆಟ್ ಸುಮಾರ್ 400 ಮೂಲ ಚಿಹ್ನೆಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ನಂಬಿಕೆ ಉಂಡು, ಉಂದೆಟ್ ಮಸ್ತ್ ಬದಲಾವಣೆಲು ಉಂಡು. ಚಿಹ್ನೆಲು ಪದೊಕುಲೆಗ್ ಬೊಕ್ಕ ಉಚ್ಚಾರೊಲೆಗ್ ನಿಲೆಯಾದಿತ್ತ್ ದ್ ಉಪ್ಪೊಲಿ. ಬರವುದ ದಿಕ್ಕು ಸಾಮಾನ್ಯವಾದ್ ಬಲತ್ತ ಕಡೆಟ್ದ್ ಎಡತ್ತ್ ಗ್ ಇತ್ತ್ಂಡ್. ಹೆಚ್ಚಿನ ಶಾಸನೊಲು ಮುದ್ರೆಲೆಡ್ (ಬಹುತೇಕ ಕಲ್ಲ್ ಡ್ದ್ ಮಲ್ಪುನವು) ಬೊಕ್ಕ ಮುದ್ರೆಲೆಡ್ (ಮಣ್ಣ್ ದ ತುಂಡುಲೆಡ್ ಮುದ್ರೆನ್ ಒತ್ತುದು ಅಯಿತ ಗುರುತುನ್ ಬುಡ್ಪೆರ್) ಉಪ್ಪುಂಡು. ಕೆಲವು ಶಾಸನೊಲು ತಾಮ್ರದ ಫಲಕೊಲೆಡ್, ಕಂಚಿದ ಉಪಕರಣೊಲೆಡ್, ಬೊಕ್ಕ ಟೆರಾಕೊಟಾ, ಕಲ್ಲ್ ಬೊಕ್ಕ ಫೇಯೆನ್ಸ್ ಡ್ ಮಲ್ತಿನ ಎಲ್ಯ ವಸ್ತುಲೆಡ್ಲಾ ತೋಜುಂಡು. ಈ ಮುದ್ರೆಲೆನ್ ವ್ಯಾಪಾರೊಡು ಬೊಕ್ಕ ಅಧಿಕೃತ ಆಡಳಿತ ಕೆಲಸೊಗುಲಾ ಬಳಕೆ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಮೊಹೆಂಜೊದಾರೊ ಬೊಕ್ಕ ಬೇತೆ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ಜಾಗೆಲೆಡ್ ಮಸ್ತ್ ಕೆತ್ತನೆದ ಸಾಮಗ್ರಿಲೆನ್ ತೂಯೆರ್.<ref>https://web.archive.org/web/20110903114242/http://asi.nic.in/asi_exca_2007_dholavira.asp</ref> ==ಸೈನ್ ಬೋರ್ಡ್== [[File:The 'Ten Indus Scripts' discovered near the northern gateway of the Dholavira citadel.jpg|thumb|Ten Indus characters from the northern gate of Dholavira, dubbed the Dholavira Signboard, one of the longest known sequences of Indus characters]] ಧೋಲಾವಿರೊಡು ಮಸ್ತ್ ಮಹತ್ವದ ಅನ್ವೇಷಣೆಲು ನಗರೊದ ಉತ್ತರ ದ್ವಾರೊದ ಒಂಜಿ ಸೈಡ್ ರೂಮ್ ಡ್ ನಡತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಧೋಲಾವಿರ ಸೈನ್ಬೋರ್ಡ್ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ಹರಪ್ಪೆರ್ ಖನಿಜ ಜಿಪ್ಸಮ್ ದ ತುಂಡುಲೆನ್ ಒಂಜಿ ಮಲ್ಲ ಮರತ್ತ ಫಲಕೊಡು ಪತ್ತ್ ಮಲ್ಲ ಚಿಹ್ನೆಲೆನ್ ಅತ್ತಂಡ ಅಕ್ಷರೊಲೆನ್ ರೂಪಿಸೆರೆ ಜೋಡಾದ್ ದೀತೆರ್. ಒಂಜಿ ಸರ್ತಿ, ಫಲಕ ಅಯಿತ ಮೋನೆಡ್ ಫ್ಲಾಟ್ ಆದ್ ಬೂರುಂಡು. ಮರ ಕುಸಿತ್ಂಡ್, ಆಂಡ ಅಕ್ಷರೊಲೆನ ವ್ಯವಸ್ಥೆ ಒರಿದ್ಂಡ್. ಸೈನ್ ಬೋರ್ಡ್ ದ ಅಕ್ಷರೊಲು ಸುತ್ತಮುತ್ತದ ಗೋಡೆಲೆಡ್ ಬಳಕೆ ಆಯಿನ ಮಲ್ಲ ಇಟ್ಟಿಗೆಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಪ್ರತಿಯೊಂಜಿ ಚಿಹ್ನೆ ಸುಮಾರ್ 37 ಸೆಂ (15 ಇಂಚ್) ಎತ್ತರೊಡು ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಬೋರ್ಡ್ ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದ ಉಪ್ಪುಂಡು. ಈ ಶಾಸನ ಸಿಂಧು ಲಿಪಿಟ್ ಅತಿ ಉದ್ದೊದ ಶಾಸನೊಲೆಡ್ ಒಂಜಿ, ಒಂಜಿ ಚಿಹ್ನೆ ನಾಲ್ ಸರ್ತಿ ತೋಜುಂಡು, ಬೊಕ್ಕ ಅಯಿತ ಮಲ್ಲ ಗಾತ್ರ ಬೊಕ್ಕ ಸಾರ್ವಜನಿಕ ಸ್ವಭಾವೊ ಸಿಂಧು ಲಿಪಿ ಪೂರ್ತಿ ಸಾಕ್ಷರತೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಈ ಜಾಗೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ಲ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಒಂಜಿ ನಾಲ್ ಚಿಹ್ನೆದ ಶಾಸನೊನುಲಾ ತೂವೊಲಿ, ಉಂದು ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಇಂಚಿನ ಶಾಸನೊಲೆಡ್ ಸುರುತವು ಆದುಂಡು ಪನ್ಪೆರ್. [[ವರ್ಗ:Event:Heritage Across Borders]] pz4m3rpsv0i5kurfvxfgc423b06lerl 366202 366200 2026-08-21T10:26:41Z Kavitha G. Kana 1040 /* ಅನ್ವೇಷಣೆ */ 366202 wikitext text/x-wiki [[File:DHOLAVIRA SITE (24).jpg|thumb|ಧೋಲಾವಿರ ಜಾಗೆ (24)]] ಧೋಲಾವಿರ ಪಂಡ ಪಶ್ಚಿಮ ಭಾರತೊದ [[ಗುಜರಾತ್]] ರಾಜ್ಯೊದ [[ಕಚ್]] ಜಿಲ್ಲೆದ ಭಾಚೌ ತಾಲೂಕುದ ಖಾದಿರ್ಬೆಟ್ ಡ್ ಉಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಜಾಗೆ, ಉಂದೆತ ಪುದರ್ ದಕ್ಷಿಣೊಡು 1 ಕಿಲೋಮೀಟರ್ (0.62 ಮೈಲ್) ದೂರೊಡು ಉಪ್ಪುನ ಇತ್ತೆದ ಗ್ರಾಮೊದ ಪುದರ್ ಡ್ ಉಂಡು. ಈ ಊರು ರಾಧನ್ಪುರ್ ಡ್ದ್ 165 ಕಿ.ಮೀ (103 ಮೈಲ್) ದೂರೊಡು ಉಂಡು. ಸ್ಥಳೀಯವಾದ್ ಕೋಟದ ಟಿಂಬಾ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್, ಈ ಜಾಗೆಡ್ ಪ್ರಾಚೀನ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ನಗರೊದ ಅವಶೇಷೊಲು ಉಂಡು. ಭೂಕಂಪೊಲು ಧೋಲಾವಿರ ಪನ್ಪಿನ ಈ ಜಾಗೆಗ್ ಸುಮಾರ್ ಸರ್ತಿ ಅಯಿತ ಕೆಟ್ಟ ಪರಿಣಾಮೊನ್ ಬೀರ್ದ್ಂಡ್ .<ref>https://www.thehindu.com/society/ruins-on-the-tropic-of-cancer/article19284214.ece</ref> ==ಜಾಗೆ== ಧೋಲಾವಿರ ಪನ್ಪಿನ ಜಾಗೆ ಉಷ್ಣವಲಯೊಡು(Tropic of cancer) ಉಂಡು. ಉಂದು ಐನ್ ಮಲ್ಲ ಹರಪ್ಪನ್ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಬೊಕ್ಕ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆಗ್ ಸೇರ್ದಿನ ಭಾರತೊದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಪಿರಾಕ್ದ ತಾಣ. ಉಂದು ಕಚ್ ಮರುಭೂಮಿದ ವನ್ಯಜೀವಿ ಅಭಯಾರಣ್ಯೊಡು ಕಚ್ ಗ್ರೇಟ್ ರನ್ ಡ್ ಖಾದಿರ್ ಬೆಟ್ ದ್ವೀಪೊಡು ಉಂಡು. ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಚತುರ್ಭುಜಾಕೃತಿದ ನಗರ ಉತ್ತರೊಡು ಮನ್ಸಾರ್ ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹಾರ್ ಪನ್ಪಿನ ಸುದೆತ್ತ ನಡುಟು ಉಂಡು. ಕ್ರಿ.ಪೂ ೧೪೫೦; ಆಂಡಲಾ, ಇತ್ತೆದ ಸಂಶೋಧನೆ ಸುಮಾರ್ ಕ್ರಿ.ಪೂ.೩೫೦೦ಡ್ (ಹರಪ್ಪನ್ ಪೂರ್ವ) ಆಕ್ರಮಣದ ಸುರುತ ಬೊಕ್ಕ ಕ್ರಿ.ಪೂ ೧೮೦೦ (ಹರಪ್ಪನ್ ಕಾಲೊದ ಸುರುತ ಭಾಗ) ಮುಟ್ಟ ನಿರಂತರತೆನ್ ಸೂಚಿಸವುಂಡು ==ಅನ್ವೇಷಣೆ== ಈ ಜಾಗೆನ್ ಸುರುಕು ಧೋಲಾವಿರ ಗ್ರಾಮದ ನಿವಾಸಿ ಶಂಭೂದನ್ ಗಾಧವಿ 1960 ದ ಸುರುತ ಭಾಗೊಡು ಕಂಡು ಕನತ್ದ್, ಸರ್ಕಾರದ ಗಮನೊನು ಈ ಜಾಗೆಗ್ ಕನರೆ ಪ್ರಯತ್ನ ಮಲ್ತೆರ್. ಈ ಜಾಗೆನ್ 1967 ಅತ್ತ್ಂಡ 1968ಡ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ದ ಜೆ.ಪಿ.ಜೋಶಿ "ಅಧಿಕೃತವಾದ್" ಬೊಳ್ಪುಗು ಕನತೆರ್, ಬೊಕ್ಕ ಉಂದು ಎಣ್ಮ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಐನ್ ನೇ ಮಲ್ಲ ಜಾಗೆ. ಉಂದು ೧೯೯೦ಡ್ದ್ ಎಎಸ್ಐಡ್ದ್ ಉತ್ಖನನೊಡು ಉಂಡು, ಉಂದು "ಧೋಲಾವಿರ ಸಿಂಧು ಕಣಿವೆದ ನಾಗರಿಕತೆದ ಅರ್ಥ ಮಲ್ಪುನಗ ನಿಜವಾದ್‌ಲಾ ಪೊಸ ಆಯಾಮೊಲೆನ್ ಸೇರ್ಸಾದ್ಂಡ್" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ತೋಜಾದ್ಂಡ್. ಧೋಲಾವಿರಾನ್ ಕಚ್‌ದ ಅತ್ಯಂತ ಪ್ರಮುಖ ಮೆಗಾಲಿಥಿಕ್(ಶಿಲಾಯುಗ) ತಾಣೊಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಗುರುತಿಸವೊಂಡುಂಡು, ಆಂಡ ಕಚ್ಚ್‌ಡ್ ತೆರಿನ ಒಂಜೇ ಒಂಜಿ ಮೆಗಾಲಿಥಿಕ್ ತಾಣ ಧನೆತಿ ಗ್ರಾಮದ ಸಮೀಪೊಡು ಉಂಡು. ಇತ್ತೆ ಮುಟ್ಟ ತೋಜುದು ಬತ್ತಿನ ಬೇತೆ ಪ್ರಮುಖ ಹರಪ್ಪನ್ ತಾಣೊಲು ಹರಪ್ಪಾ, [[ಮೊಹೆಂಜೊದಾರೊ,]] ಗಣೇರಿವಾಳ, ರಾಖಿಗಡಿ, ಕಾಳಿಬಂಗನ್, ರೂಪನಗರ ಬೊಕ್ಕ ಲೋಥಲ್.ಜುಲೈ ೨೭, ೨೦೨೧ಡ್ ಧೋಲಾವಿರ: ಒಂಜಿ ಹರಪ್ಪನ್ ನಗರ ಪನ್ಪಿನ ಪುದರ್ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಪುದರ್ ದೀಯೆರ್ ==ಉತ್ಖನನೊಲು== ಬಿಷ್ಟ್ ನ ನಿರ್ದೇಶನೊಡು ಎಎಸ್ಐ 1989ಡ್ ಉತ್ಖನನ ಸುರು ಮಲ್ತ್ಂಡ್, ಬೊಕ್ಕ 1990 ಬೊಕ್ಕ 2005 ನಡುಟು 13 ಕ್ಷೇತ್ರ ಉತ್ಖನನೊಲು ನಡತ್ಂಡ್. ಉತ್ಖನನ ನಗರ ಯೋಜನೆ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಬೊಲ್ಪುಗು ಕನೆತೆರ್, ಬೊಕ್ಕ ಪ್ರಾಣಿಲೆನ ಮೂಳೆಲು, ಚಿನ್ನ, ಬೊಳ್ಳಿ, ಟೆರ್ನಾಕೊಟರ್, ಬೊಕ್ಕ ಕಂಚಿದಂಚಿನ ಮಸ್ತ್ ಸಂಖ್ಯೆಡ್ ಪ್ರಾಚೀನ ವಸ್ತುಲೆನ್ ಕಂಡು ಕನತ್ಂಡ್. ದಕ್ಷಿಣ ಗುಜರಾತ್, ಸಿಂಧ್ ಬೊಕ್ಕ ಪಂಜಾಬ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿ ಏಷ್ಯಾದ ವಸತಿ ಸ್ಥಾನೊಲೆನ ನಡುಟು ಧೋಲಾವಿರ ಒಂಜಿ ಪ್ರಮುಖ ವ್ಯಾಪಾರ ಕೇಂದ್ರ ಆದಿತ್ತ್ಂಡ್ ಪಂದ್ ಪುರಾತತ್ತ್ವಜ್ಞೆರ್ ನಂಬುವೆರ್. ==ವಾಸ್ತುಶಿಲ್ಪ== [[ಲೋಥಲ್]] ಬಂದರ್-ನಗರೊಡ್ದ್ ಪಿರಾಕ್ ದವು ಪನ್ಪಿನ ಅಂದಾಜಿಡ್, ಧೋಲಾವಿರಾ ನಗರೊಗು ಆಯತಾಕಾರೊದ ಆಕಾರೊ ಬೊಕ್ಕ ಸಂಘಟನೆ ಉಂಡು, ಬೊಕ್ಕ ಉಂದು 22 ಹೆಕ್ಟೇರ್ (54 ಎಕ್ರೆ) ಡ್ ವ್ಯಾಪಿಸಾಯಿನವು. ಈ ಪ್ರದೇಶೊದ ಉದ್ದ 771.1 ಮೀ (2,530 ಅಡಿ), ಬೊಕ್ಕ ಅಗಲ 616.85 ಮೀ (2,023.8 ಅಡಿ) ಉಂಡು. ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊದಾರೊದಲೆಕ ಅತ್ತ್, ಈ ನಗರೊನು ಮೂಜಿ ವಿಭಾಗೊಲೆನ್ ಸೇರ್ದಂಚಿನ ಒಂಜಿ ದುಂಬೇ ಇತ್ತಿನ ಜ್ಯಾಮಿತೀಯ ಯೋಜನೆಡ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್ - ಮಿತ್ತ ಕೋಟೆ,ನಡುತ್ತ ಊರು, ಬೊಕ್ಕ ತಿರ್ತದ ಊರು. ಅಕ್ರೊಪೊಲಿಸ್ ಬೊಕ್ಕ ನಡುತ್ತ ಊರು ಅಕ್ಲೆನ ಸ್ವಂತ ರಕ್ಷಣಾ ಕೆಲಸ, ದ್ವಾರೊಲು, ನಿರ್ಮಾಣ ಆಯಿನ ಪ್ರದೇಶೊಲು,ಸಾದಿ ವ್ಯವಸ್ಥೆ,ಗುವೆಲ್ ಬೊಕ್ಕ ಮಲ್ಲ ತೆರೆದಿಪ್ಪುನ ಜಾಗೊಲೆನ್ ಸಜ್ಜು ಮಲ್ತೆರ್.ಉಂದೆಟ್ ನೈಋತ್ಯ ವಲಯೊದ ಪ್ರಮುಖ ಭಾಗೊನು ಸ್ವಾಧೀನ ಮಲ್ಪುಂಡು. ಎತ್ತರದ "ಕೋಟೆ" ನಿಲೆಲೆನ್ ಡಬಲ್ ರಾಂಪಾರ್ಟ್ ಲೆಡ್ ರಕ್ಷಣೆ ಮಲ್ಪುಂಡು. ಉಂದೆತ ಬರಿಟ್ 'ಬೈಲಿ' ಪನ್ಪಿನ ಜಾಗೆ ಉಂಡು ಅಲ್ಪ ಪ್ರಮುಖ ಅಧಿಕಾರಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಸಾಮಾನ್ಯ ಕೋಟೆಲೆನ ಉಳಯಿ ಉಪ್ಪುನ ನಗರ ಪ್ರದೇಸೊ 48 ಹೆಕ್ಟೇರ್ (120 ಎಕ್ರೆ) ಉಂಡು. ಕೋಟೆದ ವಸಾಹತುಗು ಅವಿಭಾಜ್ಯವಾದ್ ಉಪ್ಪುನ ವಿಸ್ತಾರವಾದ್ ರಚನೆ-ಬೇರಿಂಗ್ ಪ್ರದೇಶೊಲು ಉಂಡು. ಗೋಡೆಲೆನ ಮಿತ್ತ್, ಬೇತೆ ಒಂಜಿ ವಸಾಹತು ತೋಜಿದ್ಬತ್ತ್ಂಡ್. ನಗರೊದ ಅತ್ಯಂತ ಗಮನಾರ್ಹ ಲಕ್ಷಣ ಪಂಡ ಅಯಿತ ಮಾತಾ ಕಟ್ಟಡೊಲು, ಕನಿಷ್ಠ ಅಯಿತ ಇತ್ತೆದ ಸಂರಕ್ಷಣೆದ ಸ್ಥಿತಿಡ್, ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್, ಆಂಡ ಹರಪ್ಪಾ ಬೊಕ್ಕ ಮೊಹೆಂಜೊ-ದಾರೊ ಸೇರ್ದ್ ಬೇತೆ ಹರಪ್ಪನ್ ತಾಣೊಲು ಮಾತಲಾ ಇಟ್ಟಿಗೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಧೋಲಾವಿರಾ ರಡ್ಡ್ ತುಫಾನ್ ನೀರ್ದ ಕಾಲುವೆಲೆಡ್ದ್ ಸುತ್ತುವರಿದ್ ಉಂಡು; ಉತ್ತರೊಡು ಮನ್ಸರ್, ಬೊಕ್ಕ ದಕ್ಷಿಣೊಡು ಮನ್ಹರ್. ಊರುದ ಚೌಕೊಡು, ನೆಲತ ಮಿತ್ತ್ ಎತ್ತರೊಡು ಒಂಜಿ ಜಾಗೆ ಉಂಡು, ಅವೆನ್ "ಕೋಟೆ" ಪನ್ಪೆರ್.<ref>https://web.archive.org/web/20170812062930/https://archaeologynewsnetwork.blogspot.com/2010/06/rise-and-fall-of-harappan-city.html</ref> ==ಜಲಾಶಯೊಲು== ಎಎಸ್ಐ ದ ಜಂಟಿ ಮಹಾನಿರ್ದೇಶಕೆರಾದ್ ನಿವೃತ್ತರಾಯಿನ ಬಿಷ್ತ್ ಪನ್ಪೆರ್, "ನೀರ್ ದ ಸಂರಕ್ಷಣೆ, ಕೊಯ್ಲು ಬೊಕ್ಕ ಸಂಗ್ರಹೊಗು ಅಭಿವೃದ್ಧಿ ಮಲ್ತಿನ ಧೋಲಾವಿರದ ಹರಪ್ಪನೆರೆನ ಪರಿಣಾಮಕಾರಿ ವ್ಯವಸ್ಥೆ ಅಕ್ಲೆನ ಸುಧಾರಿತ ಹೈಡ್ರಾಲಿಕ್ ಎಂಜಿನಿಯರಿಂಗ್ ದ ಬಗ್ಗೆ ಪಾತೆರುಂಡು, ಕ್ರಿ.ಪೂ. ಮೂಜಿನೆ ಸಹಸ್ರಮಾನೊಡು ತಂತ್ರಜ್ಞಾನದ ಸ್ಥಿತಿನ್ ತೂಂಡ ಡೊಲಾವಿರಾದ ಸೋಫ್ಯಾಸ್ಟಿಕ್ ನೀರ್ ದ ಅನನ್ಯ ಲಕ್ಷಣೊಲು ಒಂಜಿ." ಚಾನೆಲ್ ಬೊಕ್ಕ ಜಲಾಶಯೊಲೆನ ಸಂರಕ್ಷಣೆದ ವ್ಯವಸ್ಥೆ, ಜಗತ್ತ್ಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಸುರುತ ವ್ಯವಸ್ಥೆ, ಉಂದು ಪೂರ್ತಿ ಕಲ್ಲ್ ಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ನಗರೊಡು ಮಲ್ಲ ಮಲ್ಲ ಜಲಾಶಯೊಲು ಇತ್ತ್ಂಡ್, ಅವುಲು ಮೂಜಿ ಜಲಾಶಯೊಲು ಬಹಿರಂಗ ಆತ್ಂಡ್. ಅವು ರಡ್ಡ್ ಗಿಟ್ಟದ ಕೆರೆಕುಲೆಡ್ದ್ ಬೇತೆ ಬೇತೆ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಬರ್ಸೊಡ್ದ್ ಬತ್ತಿನ ನೀರ್ ನ್ ಸಂಗ್ರಹ ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಆಪುಂಡು. ಉಂದು ಸ್ಪಷ್ಟವಾದ್ ಕಚ್ ದ ಮರುಭೂಮಿದ ಹವಾಮಾನ ಬೊಕ್ಕ ಪರಿಸ್ಥಿತಿಲೆಗ್ ಪ್ರತಿಕ್ರಿಯೆ ಆದ್ ಬತ್ತ್ಂಡ್, ಅಲ್ಪ ಕೆಲವು ವರ್ಷೊಲು ಬರ್ಸ ಬರ್ಪುಜಿ. ಈ ಜಾಗೆದ ಸಮೀಪೊಡು ಉತ್ತರ-ದಕ್ಷಿಣ ದಿಕ್ಕ್ ಡ್ ಪೋಪಿನ ಒಂಜಿ ಋತುಕಾಲಿಕ ನೀರ್ ನ್ ನೀರ್ ಸಂಗ್ರಹ ಮಲ್ಪರೆ ಕೆಲವು ಕಡೆಟ್ ಅಣೆಕಟ್ಟ್ ಮಲ್ತೆರ್. ೧೯೯೮ಡ್, ಈ ಜಾಗೆಡ್ ಬೇತೆ ಒಂಜಿ ಜಲಾಶಯೊನು ಕಂಡು ಕನತ್ ದೆರ್.<ref>https://www.cntraveller.in/magazine-story/india-via-gujarat/</ref> ಧೋಲಾವಿರಾದ ನಿವಾಸಿಲು ಮೂಜಿನೆ ಹಂತೊಡು ಬೇತೆ ಬೇತೆ ಗಾತ್ರೊದ ಪದಿನಾಜಿ ಅತ್ತಂಡ ಅಯಿಡ್ದ್ ಜಾಸ್ತಿ ಜಲಾಶಯೊಲೆನ್ ಸೃಷ್ಟಿ ಮಲ್ತೆರ್. ಅವುಲು ಕೆಲವು ಮಲ್ಲ ವಸಾಹತುದ ತಿರ್ತ್ ನೆಲತ ಇಳಿಜಾರು ಪ್ರದೇಸೊದ ಲಾಭ ದೆತೊನುವೆರ್ ಅವುಲು ಗುವೆಲ್ದಂಚಿನ ರಚನೆ ಮಲ್ಪುವೆರ್.. ಬೇತೆ ಜಲಾಶಯೊಲೆನ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಕೆಲವು ಕಲ್ಲ್ಡ್ ಕಟ್ಟ್ದೆರ್ . ಇತ್ತೆದ ಕೆಲಸೊಡು ರಡ್ಡ್ ಮಲ್ಲ ಜಲಾಶಯೊಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್, ಒಂಜಿ ಕೋಟೆದ ಪೂರ್ವೊಡ್ ಬೊಕ್ಕೊಂಜಿ ಅಯಿತ ತೆಂಕ್ ದಿಕ್ಕ್ ಗ್ ಅನೆಕ್ಸ್ ದ ಸಮೀಪೊಡು. ಜಲಾಶಯೊಲು ಕಲ್ಲ್ ಡ್ದ್ ಲಂಬವಾದ್ ಕಡ್ತ್ ದ್ ಉಪ್ಪುಂಡು, ಬೊಕ್ಕ ಅವು ಸುಮಾರ್ 7 ಮೀ (23 ಅಡಿ) ಆಳೊಡು ಬೊಕ್ಕ 79 ಮೀ (259 ಅಡಿ) ಉದ್ದೊಡು ಉಪ್ಪುಂಡು. ಅವು ನಗರೊನು ಸುತ್ತುವರಿದ್ ಉಪ್ಪುಂಡು, ಆಂಡ ಕೋಟೆ ಬೊಕ್ಕ ಮೀಪುನ ಸ್ನಾನಘಟ್ಟಡ್ ಕೇಂದ್ರೊಡು ಎತ್ತರದ ನೆಲಟ್ ಉಪ್ಪುಂಡು. ಒಂಜಿ ಮಲ್ಲ ಗೂವೆಲ್ ಲಾ ಉಂಡು, ಉಂದೆಟ್ ಒಂಜಿ ಕಲ್ಲ್ ಕಡ್ತ್ ದ್ ದೀತಿನ ಟ್ರಫ್ ನ್ ಒಂಜಿ ಡ್ರೈನ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುಂಡು, ಉಂದು ಒಂಜಿ ಟ್ಯಾಂಕ್ ಗ್ ನೀರ್ ನ್ ಕನಪುಂಡು.ಮೀಪುನ ಜಾಗೆಗ್ ಬರ್ಪಿನವುಲು ಮೆಟ್ಟಿಲುಲು ಉಪ್ಪುಂಡು.<ref>https://whc.unesco.org/en/tentativelists/5892/</ref> ಅಕ್ಟೋಬರ್ ೨೦೧೪ಡ್, ಒಂಜಿ ಆಯತಾಕಾರದ ಮುಟ್ಟ್ ಉಪ್ಪುನ ಗುವೆಲ್ಡ್ ಉತ್ಖನನ ಸುರು ಆಂಡ್, ಉಂದು ೭೩.೪ ಮೀ (೨೪೧ ಅಡಿ) ಉದ್ದ, ೨೯.೩ ಮೀ (೯೬ ಅಡಿ) ಅಗಲ, ಬೊಕ್ಕ ೧೦ ಮೀ (೩೩ ಅಡಿ) ಆಳೊಡು ಇತ್ತ್ಂಡ್, ಉಂದು ಮೊಹೆಂಜೇದಾರೊದ ಮಲ್ಲ ಸ್ನಾನಘಟ್ಟೊಡ್ದ್ ಮೂಜಿ ಪಾಲ್ ಮಲ್ಲವು. ==ಮುದ್ರೆ ಮಲ್ಪುನ== ಧೋಲಾವಿರಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಕೆಲವು ಮುದ್ರೆಲು, ಸ್ಟೇಜ್ III ಗ್ ಸೇರ್ದಿನವು, ಪ್ರಾಣಿಲೆನ ಚಿತ್ರೊಲೆನ್ ಮಾತ್ರ ಮೂಲು ತೂವೋಲಿ ಅತ್ತಂದೆ, ಒವ್ವೆ ರೀತಿದ ಲಿಪಿ ಇಜ್ಜಿ. ಈ ರೀತಿದ ಮುದ್ರೆಲು ಸಿಂಧು ಮುದ್ರೆ ತಯಾರಿಕೆದ ಪಿರಾಕ್ ದ ಸಂಪ್ರದಾಯೊಲೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ==ಬೇತೆ ರಚನೆಲು ಬೊಕ್ಕ ವಸ್ತುಲು== ==ಪೂರ್ವೊದ ದ್ವಾರ== ಆ ಜಾಗೆಡ್ ಒಂಜಿ ಮಲ್ಲ ವೃತ್ತಾಕಾರದ ರಚನೆ ಒಂಜಿ ಸಮಾಧಿ ಅತ್ತಂಡ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ನಂಬುನವು ತಿಕ್ಕ್ಂಡ್, ಆಂಡಲಾ ಅಯಿಟ್ ಅಸ್ಥಿಪಂಜರೊಲು ಅತ್ತಂಡ ಬೇತೆ ಮಾನವ ಅವಶೇಷೊಲು ಇಜ್ಜಿ. ಈ ರಚನೆಡ್ ಒಂಜಿ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ಆಕಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಪತ್ತ್ ರೇಡಿಯಲ್ ಮಣ್ಣ್-ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು ಉಂಡು. ಫಾಲಸ್ ಎರೆಕ್ಟಸ್ ಆಂಡ ತರೆ ಬೊಕ್ಕ ಕಾರ್ ಪಾದೊದ ತಿರ್ತ್ ಕತ್ತರಿಸಾಯಿನ ಒಂಜಿ ಪುರುಷನ ಮೃದುವಾಯಿನ ಮರಳುಕಲ್ಲುದ ಶಿಲ್ಪೊನು ಪೂರ್ವ ದ್ವಾರೊದ ಮಾರ್ಗೊಡು ತೂಯೆರ್. ಮಸ್ತ್ ಶವಸಂಸ್ಕಾರದ ರಚನೆಲು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್ (ಒಂಜಿ ಬುಕ್ಕೊ ಮಾತಾ ಅಸ್ಥಿಪಂಜರೊಲು ಇಜ್ಜಿ), ಅಂಚನೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಟೆರಾಕೋಟಾ ಮುದ್ರೆಲು, ಬಳೆ, ಉಂಗುರೊಲು, ಮಣಿ, ಬೊಕ್ಕ ಕೆತ್ತನೆ. ಡಾನ್ಸಿಂಗ್ ಗರ್ಲ್ ಪಂಡ ಹರಪ್ಪನ್ ನಾಗರಿಕತೆದ ಅತ್ಯಂತ ಪ್ರತಿಷ್ಠಿತ ಕಲಾಕೃತಿಲೆಡ್ ಒಂಜಿ, ಮೊಹೆಂಜೊದಾರೊಡು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಮಾರ್ ಕ್ರಿ.ಪೂ. 2500 ಡ್ದ್ ದುಂಬುದ ಈ ಕಂಚಿದ ಪ್ರತಿಮೆ ಸುಮಾರ್ 10.5 ಸೆಂಟಿಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು ಬೊಕ್ಕ ಒರಿ ಜವನೆರೆನ ನರ್ತಕಿನ ಕ್ರಿಯಾತ್ಮಕ ಶಕ್ತಿ ಬೊಕ್ಕ ಅನುಗ್ರಹೊನು ಸ್ಪಷ್ಟವಾದ್ ಸೆರೆಪತ್ತ್ದ್ಂಡ್. ==ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲು== [[File:Cicular houses Dholavira.jpg|thumb|ಧೋಲಾವಿರದ ಗೋಲಾಕಾರದ ಇಲ್ಲುಲು - ಹರಪ್ಪನ್ ಊರು]] ಧೋಲಾವಿರಾಡ್ ಏಳ್ ಅರ್ಧಗೋಳಾಕಾರದ ನಿರ್ಮಾಣೊಲೆನ್ ತೂಯೆರ್, ಅವುಲು ರಡ್ಡ್ ನ್ ವಿವರವಾದ್ ಉತ್ಖನನ ಮಲ್ತೆರ್, ಅವು ಮಲ್ಲ ಕಲ್ಲ್ ಕತ್ತರಿಸಾಯಿನ ಕೋಣೆಲೆಡ್ ನಿರ್ಮಾಣ ಆತ್ಂಡ್. ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಯೋಜನೆನ್ ಪಡೆಯಿನವು, ಅವು ಮಲ್ಲ ಅರ್ಧಗೋಳಾಕಾರದ ಎತ್ತರದ ಮಣ್ಣ್ ದ ಇಟ್ಟಿಗೆದ ನಿರ್ಮಾಣೊಲು. ಉತ್ಖನನ ಮಲ್ತಿನ ಒಂಜಿ ರಚನೆನ್ ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೂಪೊಡು ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕೊಂಜಿಲಾ ಅದೇ ರೀತಿಡ್ ಡಿಸೈನ್ ಮಲ್ತೆರ್, ಆಂಡ ಸ್ಪೋಕ್ಸ್ ಇಜ್ಜಂದಿನ ಚಕ್ರೊದಲೆಕ. ಅವುಲು ಕುಂಬಾರಿಕೆದ ಸಮಾಧಿದ ವಸ್ತುಲು ಇತ್ತ್ಂಡಲಾ, ಒಂಜಿ ಸಮಾಧಿನ್ ಬುಡ್ದು ಬೇತೆ ಒವ್ವೆ ಅಸ್ಥಿಪಂಜರೊಲು ತೋಜಿಜಿ, ಅಲ್ಪ ಒಂಜಿ ಅಸ್ಥಿಪಂಜರ ಬೊಕ್ಕ ಒಂಜಿ ತಾಮ್ರದ ಕನ್ನಡಿ ಉಂಡು. ಒಂಜಿ ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಡ್ ಒಂಜಿ ತಾಮ್ರದ ತಂತಿಗ್ ಕೊಕ್ಕೆಲೆನ್ ಕಟ್ಟ್ ದ್ ದೀತಿನ ಸ್ಟೀಟೈಟ್ ಮಣಿಲೆನ ಕಂಕಣ, ಒಂಜಿ ಬಂಗಾರ್ದ ಬಳೆ, ಬಂಗಾರ್ ಬೊಕ್ಕ ಬೇತೆ ಮಣಿಕುಲುಲಾ ಒಂಜಿ ಅರ್ಧಗೋಳದ ರಚನೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಉತ್ಖನನೊನು ನಡಪಾಯಿನ ಭಾರತದ ಪುರಾತತ್ವ ಸಮೀಕ್ಷೆ, ಈ "ಅರ್ಧಗೋಳಾಕಾರದ ರಚನೆಲು ಪಿರಾಕ್ದ ಬೌದ್ಧ ಸ್ತೂಪೊಲೆನ್ ನೆನಪು ಮಲ್ಪುಂಡು" ಪನ್ಪಿನ ಅಭಿಪ್ರಾಯೊನು ಪನ್ಪುಂಡು. ಬೊಕ್ಕ "ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ಬೊಕ್ಕ ಅನ್ಸ್ಪೋಕ್ಡ್ ವೀಲ್ ದ ರೀತಿದ ವಿನ್ಯಾಸ ಶತಪಥ ಬ್ರಾಹ್ಮಣ ಬೊಕ್ಕ ಶುಲ್ಬ ಸೂತ್ರೊಡು ನಮೂದಿಸಾಯಿನ ಸರರತ-ಚಕ್ರ-ಸಿಟಿ ಬೊಕ್ಕ ಸಪ್ರಧಿ-ರತ-ಚಕ್ರ-ಸಿಟಿನ್ ನೆಂಪು ಮಲ್ಪುಂಡು" ==ಉತ್ತರೊದ ದ್ವಾರ== [[File:Dholavira-1.jpg|thumb|ಧೋಲಾವಿರ-1]] ಸಿಂಧು ಲಿಪಿತ ಮಿತ್ತ್ ಕಪ್ಪು ಬಣ್ಣದ ಕುಂಬಾರಿಕೆ, ಚೌಕ ಮುದ್ರೆದ ಮುದ್ರೆಲು, ಸಿಂಧು ಲಿಪಿ ಇಜ್ಜಂದಿನ ಮುದ್ರೆಲು, ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದೊದ ಒಂಜಿ ಮಲ್ಲ ಸೈನ್ ಬೋರ್ಡ್, ಸಿಂಧು ಲಿಪಿತ ಪತ್ತ್ ಅಕ್ಷರೊಲೆನ್ ಸೇರ್ದುಂಡು. ಕಲ್ಲ್ ಡ್ ಮಲ್ತಿನ ಒಂಜಿ ಕಳಪೆ ರೀತಿಡ್ ಸಂರಕ್ಷಣೆ ಆಯಿನ ಕುಲ್ಲುನ ಪುರುಷ ರೂಪೊನುಲಾ ತೂಯೆರ್, ಉಂದು ಹರಪ್ಪಾಡ್ ತೋಜಿದ್ ಬತ್ತಿನ ಉನ್ನತ ಗುಣಮಟ್ಟದ ರಡ್ಡ್ ಕಲ್ಲ್ ದ ಶಿಲ್ಪೊಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಮುಳ್ಳುದ ನೆಲತ ಮಲ್ಲ ಕಪ್ಪು ಜಾರ್ ಲೆನ್ ಲಾ ಈ ಜಾಗೆಡ್ ತೂಯೆರ್. ಒಂಜಿ ಮಲ್ಲ ಕಂಚಿದ ಸುತ್ತಿಗೆ, ಒಂಜಿ ಮಲ್ಲ ಉಳಿ, ಒಂಜಿ ಕಂಚಿನ ಕೈಟ್ ಪತ್ತೊಂದಿಪ್ಪುನ ಕನ್ನಡಿ, ಒಂಜಿ ಬಂಗಾರ್ದ ತಂತಿ, ಬಂಗಾರ್ದ ಕೆಬಿ ಓಲೆ, ತಾಮ್ರದ ಸೆಲ್ಟ್ಸ್ ಬೊಕ್ಕ ಚಿಪ್ಪುದ ಬಳೆ, ಕಲ್ಲ್ ದ ಫಾಲಸ್ ಲೆಕೊದ ಚಿಹ್ನೆಲು, ಸಿಂಧು ಶಾಸನ ಬೊಕ್ಕ ಚಿಹ್ನೆಲೆನ್ ದೀದ್ ಚೌಕ ಮುದ್ರೆಲು, ಒಂಜಿ ವೃತ್ತಾಕಾರದ ಮುದ್ರೆ, ಕುಂಟುದ ಪ್ರಾಣಿಲು, ಪಾಟರ್ ಪೇಂಟೆಡ್ ಪಾಟರ್ಡ್, ಗೊಬ್ಬುಲು, ಡಿಶ್-ಆನ್-ಸ್ಟ್ಯಾಂಡ್, ರಂದ್ರೊಡುಪ್ಪುನ ಜಾರ್ ಲು, ಎಡ್ಡೆ ಆಕಾರೊಡು ಉಪ್ಪುನ ಟೆರಾಕೊಟಾ ಟಂಬ್ಲರ್ ಲು,ಕಡೆಪ್ಪುನ ಕಲ್ಲ್, ಗಾರೆಲು ಇಂಚಿನವುಲಾ ಈ ಜಾಗೆಡ್ ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಬೇತೆ ಬೇತೆ ಅಳತೆದ ಕಲ್ಲ್ ದ ತೂಕೊಲೆನ್ ಲಾ ತೂಯೆರ್. ==ಕರಾವಳಿ ಮಾರ್ಗ== ಲೋಥಲ್ ಬೊಕ್ಕ ಧೋಲಾವಿರಾನ್ ಮಕ್ರಾನ್ ಕರಾವಳಿಡ್ ಸುತ್ಕಗನ್ ದೋರ್ ಗ್ ಸಂಪರ್ಕ ಮಲ್ಪುನ ಒಂಜಿ ಕರಾವಳಿ ಮಾರ್ಗ ಉಂಡು. ==ಬಾಸೆ ಬೊಕ್ಕ ಲಿಪಿ == ಹರಪ್ಪನೆರ್ ಒಂಜಿ ಅಜ್ಞಾತ ಬಾಸೆನ್ ಪಾತೆರೊಂದಿತ್ತೆರ್ ಬೊಕ್ಕ ಅಕ್ಲೆನ ಲಿಪಿನ್ ಇತ್ತೆಲಾ ಅರ್ಥ ಮಲ್ತೊಂದಿಜ್ಜೆರ್. ಉಂದೆಟ್ ಸುಮಾರ್ 400 ಮೂಲ ಚಿಹ್ನೆಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನ ನಂಬಿಕೆ ಉಂಡು, ಉಂದೆಟ್ ಮಸ್ತ್ ಬದಲಾವಣೆಲು ಉಂಡು. ಚಿಹ್ನೆಲು ಪದೊಕುಲೆಗ್ ಬೊಕ್ಕ ಉಚ್ಚಾರೊಲೆಗ್ ನಿಲೆಯಾದಿತ್ತ್ ದ್ ಉಪ್ಪೊಲಿ. ಬರವುದ ದಿಕ್ಕು ಸಾಮಾನ್ಯವಾದ್ ಬಲತ್ತ ಕಡೆಟ್ದ್ ಎಡತ್ತ್ ಗ್ ಇತ್ತ್ಂಡ್. ಹೆಚ್ಚಿನ ಶಾಸನೊಲು ಮುದ್ರೆಲೆಡ್ (ಬಹುತೇಕ ಕಲ್ಲ್ ಡ್ದ್ ಮಲ್ಪುನವು) ಬೊಕ್ಕ ಮುದ್ರೆಲೆಡ್ (ಮಣ್ಣ್ ದ ತುಂಡುಲೆಡ್ ಮುದ್ರೆನ್ ಒತ್ತುದು ಅಯಿತ ಗುರುತುನ್ ಬುಡ್ಪೆರ್) ಉಪ್ಪುಂಡು. ಕೆಲವು ಶಾಸನೊಲು ತಾಮ್ರದ ಫಲಕೊಲೆಡ್, ಕಂಚಿದ ಉಪಕರಣೊಲೆಡ್, ಬೊಕ್ಕ ಟೆರಾಕೊಟಾ, ಕಲ್ಲ್ ಬೊಕ್ಕ ಫೇಯೆನ್ಸ್ ಡ್ ಮಲ್ತಿನ ಎಲ್ಯ ವಸ್ತುಲೆಡ್ಲಾ ತೋಜುಂಡು. ಈ ಮುದ್ರೆಲೆನ್ ವ್ಯಾಪಾರೊಡು ಬೊಕ್ಕ ಅಧಿಕೃತ ಆಡಳಿತ ಕೆಲಸೊಗುಲಾ ಬಳಕೆ ಮಲ್ತೊಂದಿತ್ತೆರ್. ಮೊಹೆಂಜೊದಾರೊ ಬೊಕ್ಕ ಬೇತೆ ಸಿಂಧು ಕಣಿವೆ ನಾಗರಿಕತೆದ ಜಾಗೆಲೆಡ್ ಮಸ್ತ್ ಕೆತ್ತನೆದ ಸಾಮಗ್ರಿಲೆನ್ ತೂಯೆರ್.<ref>https://web.archive.org/web/20110903114242/http://asi.nic.in/asi_exca_2007_dholavira.asp</ref> ==ಸೈನ್ ಬೋರ್ಡ್== [[File:The 'Ten Indus Scripts' discovered near the northern gateway of the Dholavira citadel.jpg|thumb|Ten Indus characters from the northern gate of Dholavira, dubbed the Dholavira Signboard, one of the longest known sequences of Indus characters]] ಧೋಲಾವಿರೊಡು ಮಸ್ತ್ ಮಹತ್ವದ ಅನ್ವೇಷಣೆಲು ನಗರೊದ ಉತ್ತರ ದ್ವಾರೊದ ಒಂಜಿ ಸೈಡ್ ರೂಮ್ ಡ್ ನಡತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಧೋಲಾವಿರ ಸೈನ್ಬೋರ್ಡ್ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. ಹರಪ್ಪೆರ್ ಖನಿಜ ಜಿಪ್ಸಮ್ ದ ತುಂಡುಲೆನ್ ಒಂಜಿ ಮಲ್ಲ ಮರತ್ತ ಫಲಕೊಡು ಪತ್ತ್ ಮಲ್ಲ ಚಿಹ್ನೆಲೆನ್ ಅತ್ತಂಡ ಅಕ್ಷರೊಲೆನ್ ರೂಪಿಸೆರೆ ಜೋಡಾದ್ ದೀತೆರ್. ಒಂಜಿ ಸರ್ತಿ, ಫಲಕ ಅಯಿತ ಮೋನೆಡ್ ಫ್ಲಾಟ್ ಆದ್ ಬೂರುಂಡು. ಮರ ಕುಸಿತ್ಂಡ್, ಆಂಡ ಅಕ್ಷರೊಲೆನ ವ್ಯವಸ್ಥೆ ಒರಿದ್ಂಡ್. ಸೈನ್ ಬೋರ್ಡ್ ದ ಅಕ್ಷರೊಲು ಸುತ್ತಮುತ್ತದ ಗೋಡೆಲೆಡ್ ಬಳಕೆ ಆಯಿನ ಮಲ್ಲ ಇಟ್ಟಿಗೆಲೆಗ್ ಹೋಲಿಕೆ ಆಪುಂಡು. ಪ್ರತಿಯೊಂಜಿ ಚಿಹ್ನೆ ಸುಮಾರ್ 37 ಸೆಂ (15 ಇಂಚ್) ಎತ್ತರೊಡು ಉಪ್ಪುಂಡು ಬೊಕ್ಕ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಬೋರ್ಡ್ ಸುಮಾರ್ 3 ಮೀ (9.8 ಅಡಿ) ಉದ್ದ ಉಪ್ಪುಂಡು. ಈ ಶಾಸನ ಸಿಂಧು ಲಿಪಿಟ್ ಅತಿ ಉದ್ದೊದ ಶಾಸನೊಲೆಡ್ ಒಂಜಿ, ಒಂಜಿ ಚಿಹ್ನೆ ನಾಲ್ ಸರ್ತಿ ತೋಜುಂಡು, ಬೊಕ್ಕ ಅಯಿತ ಮಲ್ಲ ಗಾತ್ರ ಬೊಕ್ಕ ಸಾರ್ವಜನಿಕ ಸ್ವಭಾವೊ ಸಿಂಧು ಲಿಪಿ ಪೂರ್ತಿ ಸಾಕ್ಷರತೆನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಈ ಜಾಗೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ಲ ಅಕ್ಷರೊಲೆನ್ ಬರೆತಿನ ಒಂಜಿ ನಾಲ್ ಚಿಹ್ನೆದ ಶಾಸನೊನುಲಾ ತೂವೊಲಿ, ಉಂದು ಹರಪ್ಪನ್ ಜಾಗೆಲೆಡ್ ಮರಳುಕಲ್ಲ್ ಡ್ ಇಂಚಿನ ಶಾಸನೊಲೆಡ್ ಸುರುತವು ಆದುಂಡು ಪನ್ಪೆರ್. [[ವರ್ಗ:Event:Heritage Across Borders]] 3wm2mne60lr333us3broud8ivyiiyqe ರಾಮಪ್ಪ ದೇವಸ್ಥಾನ 0 28020 366088 365934 2026-08-20T16:09:04Z Pragathi. BH 6361 366088 wikitext text/x-wiki {{Short description|13th century Kakatiya Hindu temple in Telangana}} {{Redirect|Ramappa|the lake|ರಾಮಪ್ಪ ಸರೋವರ}} {{Use dmy dates|date=September 2018}} {{Use Indian English|date=September 2018}} {{Infobox temple | name = ರಾಮಪ್ಪ ದೇವಸ್ಥಾನ | image = ರಾಮಪ್ಪ ದೇವಸ್ಥಾನ (Human Scale).jpg | alt = ರುದ್ರೇಶ್ವರ ದೇವಸ್ಥಾನ | caption = | pushpin_map = ಭಾರತ ತೆಲಂಗಾಣ | pushpin_caption = Location in ತೆಲಂಗಾಣ | coordinates = {{coord|18|15|33|N|79|56|36|E|scale:5000|display=title}} | country = ಭಾರತ | state = ತೆಲಂಗಾಣ | district = ಮುಲುಗು ಜಿಲ್ಲೆ | location = ಪಾಲಂಪೇಟ್ ಗ್ರಾಮ | elevation_m = | deity = Shiva as Ramalingeshwara Swamy<ref name="hin1" /> | festival = ಮಹಾ ಶಿವರಾತ್ರಿ | architect = ರಾಮಪ್ಪ | architecture = ಕಾಕತೀಯ ವಾಸ್ತು ಶಿಲ್ಪ, ಭೂಮಿಜ ವೇಸರ ಶೈಲಿ style | facade_direction = ಪೂರ್ವಾಭಿಮುಖ | temple_quantity = | monument_quantity = | inscriptions = | completed = 13ನೇ ಶತಮಾನ | creator = ರೆಚೆರ್ಲಾ ರುದ್ರ | website = <!-- DESIGNATION1 --> | designation1 = WHS | designation1_offname = Kakatiya Rudreshwara (Ramappa) Temple, Telangana | designation1_criteria = {{UNESCO WHS type|(i)(iii)}}(i)(iii) | designation1_date = 2021 <small>(44th [[World Heritage Committee|session]])</small> | designation1_number = [https://whc.unesco.org/en/list/1570 1570] <!-- FOOTNOTES --> | footnotes = | religious_affiliation = ಹಿಂದೂ ಧರ್ಮ }}    '''ರಾಮಪ್ಪ ದೇವಸ್ಥಾನ''', '''ರುದ್ರೇಶ್ವರ ದೇವಸ್ಥಾನ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್, ಉಂದು [[ತೆಲಂಗಾಣ|ಭಾರತದ ತೆಲಂಗಾಣದ]] ಮುಲುಗು ಜಿಲ್ಲೆದ ಪಲಂಪೆಟ್ ಗ್ರಾಮೊಡು ಇಪ್ಪುನ ಹಿಂದೂ ದೇವೆರ್ [[ಶಿವ|ಶಿವಗ್]] ಸಮರ್ಪಿಸಾಯಿನ ಒಂಜಿ ಕಾಕತೀಯ ಶೈಲಿದ ಹಿಂದೂ ದೇವಸ್ಥಾನ . <ref>{{Cite web|date=7 July 2026|title=Telangana Ramappa Temple|url=https://www.incredibleindia.gov.in/en/telangana/warangal/ramappa-temple|accessdate=7 July 2026}}</ref> ಉಂದು {{Convert|15|km|abbr=on}}ಮುಲುಗುಡ್ದ್ , {{Convert|66|km|abbr=on}} ವಾರಂಗಲ್ ಡ್ದ್, {{Convert|209|km|abbr=on}} [[ಹೈದರಾಬಾದ್, ಭಾರತ|ಹೈದರಾಬಾದ್]] ಡ್ದ್ ದೂರಡ್ ಉಂಡು. ದೇವಸ್ಥಾನಡ್ ಉಪ್ಪುನ ಶಾಸನದ ಪ್ರಕಾರ,ನೆನ್ ಕಾಕತೀಯ ರಾಜ ಗಣಪತಿದೇವನ ಸೇನಾಧಿಪತಿ ರೆಚರ್ಲ ರುದ್ರೆ ಕ್ರಿ.ಶ. 1213ಡ್ ನಿರ್ಮಿಸಾಯೆ. ರಾಮಪ್ಪ ಕೆರೆತ ಕೈತಲ್ ಉಪ್ಪುನ ಈ ದೇವಸ್ಥಾನದ ಸಂಕೀರ್ಣಡ್ ಮೂಜಿ ದೇವಾಲಯಲು ಉಂಡು. ಈ ಸಂಕೀರ್ಣನ್ ಕ್ರಿ.ಶ. 1212ರ್ದ್ 1234ತ ನಡುಟು ನಿರ್ಮಿಸಾಯೆರ್.ದೇವಸ್ಥಾನೊನು ರಾಮಪ್ಪ ಪನ್ಪುನ ಶಿಲ್ಪಿ ವಿನ್ಯಾಸ ಮಲ್ತ್ನೆ ರ್ದಾವರ ನೆಕ್ಕ್ ರಾಮಪ್ಪ ದೇವಸ್ಥಾನ ಪನ್ಪುನ ಪುದರ್ ಬತ್ತ್ಂಡ್. [ ೫ ] <ref>{{Cite news|url=https://economictimes.indiatimes.com/magazines/panache/800-year-old-ramappa-temple-in-telangana-gets-the-unesco-world-heritage-site-tag/articleshow/84752055.cms?from=mdr|title=800-year-old Ramappa Temple in Telangana gets the UNESCO World Heritage Site tag|date=2021-07-26|work=The Economic Times|access-date=2023-10-18|issn=0013-0389}}</ref> ಮಾರ್ಕೊ ಪೋಲೊ, ಕಾಕತೀಯ ಸಾಮ್ರಾಜ್ಯೊಗು ಭೇಟಿ ಕೊರ್ನಗ, ಈ ದೇವಸ್ಥಾನೊನು "ದೇವಾಲಯೊಲೆನ ನಕ್ಷತ್ರಪುಂಜೊಡು ಅತ್ಯಂತ ಪ್ರಕಾಶಮಾನವಾಯಿನ ನಕ್ಷತ್ರ" ಪಂಡ್ದ್ ಲೆತ್ತೆರ್. ಜುಲೈ ೨೦೨೧ಡ್, ರಾಮಪ್ಪ ದೇವಸ್ಥಾನೊನು ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾಣ ಪಂಡ್ದ್ ಘೋಷಣೆ ಮಲ್ತೆರ್ . <ref>{{Cite web|last=|first=|title=Kakatiya Rudreswara (Ramappa) Temple, Telangana|url=https://whc.unesco.org/en/list/1570/|accessdate=2023-04-01|website=UNESCO World Heritage Centre|language=en}}</ref> <ref>{{Cite web|date=2021-07-26|title=Telangana's Ramappa Temple becomes a UNESCO World Heritage Site|url=https://indianexpress.com/article/lifestyle/art-and-culture/telangana-ramappa-temple-becomes-unesco-world-heritage-site-7422644/|accessdate=2023-04-01|website=[[The Indian Express]]|language=en}}</ref> == ರಚನೆ == ಈ ದೇವಸ್ಥಾನ ಒಂಜಿ ನಕ್ಷತ್ರ ಆಕಾರದ ''ಉಪಪೀಠ''(ಆಧಾರ) ದ ಮಿತ್ತ್ ಉಂಡು, ಉಂದು ಸುಮಾರ್ 6 ಅಡಿ ಎತ್ತರದ ಎತ್ತರದ ವೇದಿಕೆದ ಮಿತ್ತ್ ಉಂಡು. <ref name=":3">{{Cite journal|last=Joshi|first=Arpita|date=26 December 2021|title=The Wonder of the Kakatiya art- Ramappa temple|url=https://journal.sham.org.in/journals/volume-1/6284b1d9c2da112d9719ddc9|journal=Journal of Heritage, Archaeology & Management|volume=1|issue=2|pages=23–31}}</ref> ದೇವಸ್ಥಾನ ಮುಡಾಯಿ ಪುಡೆಕ್ ಮೋನೆ ಪಾಡ್ದುಂಡು. ಉಂದು ವಾಸ್ತುಶಾಸ್ತ್ರದ ತತ್ವೊಲೆಗ್ ಅನುಗುಣವಾದುಂಡು.''.'' <ref name=":3" /> ದೇವಸ್ಥಾನದ ಒಳಾಂಗಣೊಡು ಒಂಜಿ ''ಗರ್ಭಗೃಹ'' (ಗರ್ಭಗುಡಿ), ಒಂಜಿ ''ಅಂತರ್ಲಾ'' (ಆಂಟೆಚೇಂಬರ್) ಬೊಕ್ಕ ಒಂಜಿ ಮಲ್ಲ ಸ್ತಂಭೊಲೆನ ''ಸಭಾಮಂಟಪ'' (ಸಭೆ ಸಭಾಂಗಣ) ಉಂಡು. ''ಸಭಾಮಂಟಪೊಡು'' ಮೂಜಿ ''ಮುಖಮಂಟಪೊಲು ('' ಪ್ರವೇಶದ ಮುಖಮಂಟಪೊಲು), ''ನಾಟ್ಯಮಂಟಪ'' ಪನ್ಪಿನ ನರ್ತಕಿಲೆಗ್ ಒಂಜಿ ವೇದಿಕೆ ಬೊಕ್ಕ ''ಕಕ್ಷಸನ'' ಪನ್ಪಿನ ಒಂಜಿ ಕುಲ್ಲುನ ಜಾಗೆಲಾ ಉಪ್ಪುಂಡು. <ref name=":4">{{Cite web|date=9 July 2026|title=THE GLORIOUS KAKATIYA TEMPLES AND GATEWAYS RUDRESHWARA (RAMAPPA) TEMPLE|url=https://whc.unesco.org/document/172291|accessdate=9 July 2026}}</ref> ''ಗರ್ಭಗೃಹದ'' ಮಿತ್ತ್ ವರ್ಸಾರ ವಾಸ್ತುಶಿಲ್ಪ ಶೈಲಿದ ಐನ್ ಮಾಲಿಗೆದ ''ವಿಮಾನೊ'' ಈ ದೇವಸ್ಥಾನೊಡು ಉಂಡು. ಅಯಿತ ಮೇಲ್ ರಚನೆಡ್ ನಾಲ್ ''ಭೂಮಿಲು'' (ಸಮತಲ ಶ್ರೇಣಿಲು) ಉಪ್ಪುಂಡು, ಅಯಿಟ್ ಒಂಜಿ ''ಶಿಖರನ್ಲಾ‌ ನಿರ್ಮಿಸಾದೆರ್.'' <ref name=":4" /> ದೇವಸ್ಥಾನದ ಸಂಕೀರ್ಣೊನು ಕಲ್ಲ್ ದ ''ಪ್ರಕಾರೊಡು'' (ಪರಿಧಿ ಗೋಡೆ) ಸುತ್ತುವರಿದ್ ಉಂಡು ಬೊಕ್ಕ ಉಂದೆಟ್ ಕೆಲವು ಉಪ ದೇಗುಲೊಲು ಬೊಕ್ಕ ಅಯಿಕ್ ಸಂಬಂಧಪಟ್ಟಿನ ರಚನೆಲು ಉಂಡು. <ref name=":4" /> * ''ನಂದಿ ಮಂಟಪ : ಉಂದು ದೇವಸ್ಥಾನದ ಪ್ರವೇಶದ್ವಾರದ ಕೈತಲ್ ಉಂಡು. ಮುಲ್ಪ ಶಿವನ ಮುಖ್ಯ ಗರ್ಭಗುಡಿತ ಕಡೆಕ್ ಮೋನೆಪಾಡ್ದ್ ಉಪ್ಪುನ ನಂದಿನ ಮೂರ್ತಿ ಉಂಡು. ನಂದಿನ ಮಿತ್ತ್ ದ ಅಲಂಕಾರೊಲು ಅಂಚನೆ ದೇವಸ್ಥಾನದ ಗೋಡೆದ ಅಡಿಭಾಗೊಡು ಉಪ್ಪುನ ಪೂತ ವಿನ್ಯಾಸೊಲು, ಆನೆದ ಬೊಕ್ಕ ಸಂಗೀತಗಾರೆರ್ನ ಕೆತ್ತನೆಲು ಗಮನಾರ್ಹವಾದುಂಡು.'' <ref name=":3" /> * ''ಕಟೇಶ್ವರ'' ದೇವಸ್ಥಾನ: ಉಂದು ಮುಖ್ಯ ದೇವಸ್ಥಾನದ ಬಡಕಾಯಿ ಭಾಗೊಡು ಉಂಡು. ಕಟೇಶ್ವರನಿಗೆ ಸಮರ್ಪಿತವಾಯಿನ ಈ ಎಲ್ಯ ದೇವಸ್ಥಾನ ಎತ್ತರದ ಉಪಪೀಠದ ಮಿತ್ತ್ ನಿರ್ಮಿಸಾದೆರ್. ನೆಕ್ ಪೋಪುನ ಮುಟ್ಟುಲೆದ ರಡ್ಡ್ ಬರಿಟ್ಲಾ ಕೆತ್ತ್ ನ ಕಲ್ಲ್ ದ ಆನೆಕಾಲು ಉಂಡು . <ref name=":4" /> * ''ಕಾಮೇಶ್ವರ'' ದೇವಸ್ಥಾನ: ಉಂದು ಮುಖ್ಯ ದೇವಸ್ಥಾನದ ತೆನ್ಕಾಯಿ ಭಾಗೊಡುಂಡು. ಗರ್ಭಗುಡಿ ಇಜ್ಜಾಂದ್ನ ಕಾರಣ ಬೊಕ್ಕ ರಡ್ಡ್ ಪುಡೆಟ್ಲಾ ಒಂಜೇಲೆಕದ ಪ್ರವೇಶದ್ವಾರ ಉಪ್ಪುನೆರ್ದಾವರ ನೆನ್ ಕಲ್ಯಾಣ ಮಂಟಪ ಪಂದು ಲೆಪ್ಪುವೆರ್. ಆಂಡ ರೆಚರ್ಲ ರುದ್ರನ ಶಾಸನಡ್ ಉಂದೆನ್ ಕಾಮೇಶ್ವರ ದೇವಸ್ಥಾನ ಪಂದು ಗುರುತಿಸಾದೆರ್. <ref name=":4" /> * ಸಹಾಯಕ ರಚನೆಲು: ಮುಖ್ಯ ದೇವಸ್ಥಾನದ ನೈಋತ್ಯ ಭಾಗೊಡು ಒಂಜಿ ಎಲ್ಯ ಕೊಠಡಿ ಉಂಡು. ನೆನ್ ಇತ್ತೆ ಪಾಕಶಾಲೆ ಪಂದು ಲೆಪ್ಪುವೆರ್. ಭಕ್ತಾದಿಲೆಗ್ ಆಹಾರ ತಯಾರಿಸಾದ್ ಕೊರೆತೆ ನೆನ್ ಗಳಸೊಂದಿತ್ತೆರ್ ಪಂದು ನಂಬುದೆರ್. [[ಪೊಯ್ಯೆದ ಕಲ್ಲ್|ಮರಳುಕಲ್ಲುಲು]], ಗ್ರಾನೈಟ್ ಬೊಕ್ಕ ಡೊಲೆರೈಟ್ ಸೇರ್ದ್ ಸ್ಥಳೀಯವಾದ್ ಲಭ್ಯವಾಯಿನ ಸಾಮಗ್ರಿಲೆನ್ ಗಲಸ್ ದ್ ಈ ದೇವಸ್ಥಾನೊನು ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. <ref name=":3" /> ಗುಲಾಬಿ ಬಣ್ಣದ ಮರಳುಕಲ್ಲು ಮುಖ್ಯ ಕಟ್ಟಡದ ನಿರ್ಮಾನೊಗು ಪ್ರಮುಖವಾದ್ ಗಲಸ್ ದೆರ್. ಚೆಲ್ವಾಯಿ ಸರಣಿದ ಮಧ್ಯಮ-ಧಾನ್ಯದ ಗುಲಾಬಿ ಮರಳುಕಲ್ಲು ಮುಖ್ಯ ರಚನೆಗ್ ಪ್ರಾಥಮಿಕ ಕಟ್ಟಡ ಸಾಮಗ್ರಿ ಆದಿತ್ತ್ಂಡ್, ಆಂಡ ಗ್ರಾನೈಟ್ ನ್ ಸ್ತಂಭೊಲು, ಕಿರಣೊಲು ಬೊಕ್ಕ ಚಾವಣಿದ ಫಲಕೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್ ದಾಯೆ ಪಂಡ ಅಯಿಟ್ ದಿನ್ನೊನು ತಡೆಪುನ ಗುಣಲಕ್ಷಣೊಲು ಉಂಡು. ಡೊಲೆರೈಟ್ಸ್, ಒಂಜಿ ಅಗ್ನಿ ಕಲ್ಲ್, ಕೆತ್ತನೆದ ವಾಸ್ತುಶಿಲ್ಪದ ಅಂಶೊಲೆಗ್ ಬಳಕೆ ಆತ್ಂಡ್, ಉಂದೆಟ್ ಜೀವಂತ ಗಾತ್ರದ ಬ್ರಾಕೆಟ್ ಫಿಗರ್ಸ್, ಬಾಕಿಲ್ದ ಜಾಂಬ್ಸ್, ಬೊಕ್ಕ ಲೋಡ್-ಬೇರಿಂಗ್ ಸ್ತಂಭೊಲು ಸೇರ್ದ್ ಉಪ್ಪುಂಡು. ದೇವಸ್ಥಾನೊದ "ಸ್ಯಾಂಡ್ ಬಾಕ್ಸ್" ಅಡಿಪಾಯೊದ ವ್ಯವಸ್ಥೆಡ್ ಪೊಯ್ಯೆನ್ ಬಳಕೆ ಮಲ್ತೆರ್, ಅಲ್ಪ ಉಂದು ಭೂಕಂಪ ನಿರೋಧಕತೆನ್ ಸುಧಾರಿಸವುನ ಬೊಕ್ಕ ರಚನಾತ್ಮಕ ಭಾರೊಲೆನ ಏಕರೂಪದ ವಿತರಣೆನ್ ಉತ್ತೇಜಿಸವುನ ಒಂಜಿ ಕುಶನ್ ಆದ್ ಕಾರ್ಯನಿರ್ವಹಿಸೊಂದಿತ್ತ್ಂಡ್. ನಿರ್ಮಾಣೊಡು ಬಳಕೆ ಆಯಿನ ಪ್ರಮುಖ ತಯಾರಿಕೆದ ಸಾಮಗ್ರಿಲು ಲೈಟ್ ವೆಯಿಟ್ ಪೋರಸ್ ಇಟ್ಟಿಗೆಲು ಬೊಕ್ಕ ಕರ್ಬದ ಡೋವೆಲ್ ಲು. ಇಟ್ಟಿಗೆಲೆನ್ ಕಯೋಲಿನಿಟಿಕ್ ಮಣ್ಣ್ ಡ್ದ್ ಮಲ್ತೆರ್, ಉಂದೆನ್ ಕಾರೆ ಮರಕುಲೆಡ್ದ್ ಗಮ್, ಜೂಟ್ ಫೈಬರ್ ಬೊಕ್ಕ ಉಮಿನ್ ಬೆರೆಸಾದ್ ಮಲ್ತೆರ್. ಇಟ್ಟಿಗೆಲೆನ್ ಸೂಟ್ ಪೊತ್ತಾನಗ , ಗಮ್ ಲಾ ಪೊತ್ತುದು ಪೋದು ಏರ್ ಪಾಕೆಟ್ ಲೆನ್ ಬುಡ್ದು ಇಟ್ಟಿಗೆಲೆನ ಸಾಂದ್ರತೆನ್ ಕಮ್ಮಿ ಮಲ್ಪುಂಡು. ಈ ಲೈಟ್ ವೆಯಿಟ್ ಇಟ್ಟಿಗೆಲೆನ್ ಕಲ್ಲ್ ದ ಛಾವಣಿದ ಭಾರ ಕಮ್ಮಿ ಮಲ್ಪೆರೆ ''ವಿಮನ'' ನಿರ್ಮಾಣೊಡು ಬಳಕೆ ಮಲ್ತೆರ್. ಕಲ್ಲ್ ದ ತುಂಡುಲೆನ್ ಸುರಕ್ಷಿತವಾದ್ ದೀಯೆರೆ ಬೊಕ್ಕ ದೇವಸ್ಥಾನೊದ ರಚನಾತ್ಮಕ ಸ್ಥಿರತೆನ್ ಸುಧಾರಿಸೆರೆ ಕರ್ಬದ ಡೋವೆಲ್ ಲೆನ್ ಬಳಕೆ ಮಲ್ತೆರ್. <ref name=":4" />   == ಶಿಲ್ಪೊಲು == ದೇವಸ್ಥಾನೊದ ಜಟಿಲ ಶಿಲ್ಪೊಲೆನ್ ಡೊಲೆರೈಟ್ ಪನ್ಪಿನ ಒಂಜಿ ಗಟ್ಟಿ ಸೂಕ್ಷ್ಮ-ಧಾನ್ಯದ ಕಪ್ಪು ಬಸಾಲ್ಟ್ ಡ್ದ್ ಕತ್ತರಿಸಯೆರ್. ಈ ಕೆತ್ತನೆಲು 800 ವರ್ಸೊಡ್ದ್ ಜಾಸ್ತಿ ಕಾಲ ಕನ್ನಡಿದಂಚಿನ ಪಾಲಿಶ್ ನ್ ಒರಿತೊಂದು ಉಂಡು. <ref name=":4" /> ಶಿಲ್ಪೊಲೆನ್ ಪೌರಾಣಿಕ ಪ್ರಾಣಿಲೆನ ರೂಪೊಡು ಅತ್ತಂಡ ಪೊಣ್ಣು ನರ್ತಕಿಲೆನ ರೂಪೊಡು ಅತ್ತಂಡ ಸಂಗೀತಗಾರೆರೆನ ರೂಪೊಡು ಕೆತ್ತನೆ ಮಲ್ತೆರ್, ಬೊಕ್ಕ ಅವು "ಕಾಕತೀಯ ಕಲೆತ ಮೇರುಕೃತಿಲು, ಅಯಿಕ್ಲೆನ ಸೂಕ್ಷ್ಮ ಕೆತ್ತನೆ, ಆಕರ್ಷಕ ಭಂಗಿಲು, ಬೊಕ್ಕ ಉದ್ದೊದ ದೇಹೊಲು ಬೊಕ್ಕ ತರೆತ ವಿನ್ಯಾಸೊಗು ಗಮನಾರ್ಹವಾದುಂಡು". [ ೧೪ ] ಈ ದೇವಸ್ಥಾನ ''ಗಜ-ವ್ಯಾಲ'' ಪನ್ಪಿನ ಒಂಜಿ ಪೌರಾಣಿಕ ಜೀವಿ ಬೊಕ್ಕ ''ಮದನಿಕ ಪನ್ಪಿನ ಪೊಣ್ಣುಲೆನ ಬ್ರಾಕೆಟ್ ಚಿತ್ರೊಲೆಗ್ ಪುದರ್ ಪೋತುಂಡು.'' <ref name=":3" /> ಈ ಬ್ರಾಕೆಟ್ ಫಿಗರ್ಸ್ ''ಕಪೋಟಾನ್'' ಬೆಂಬಲಿಸವುಂಡು, ಓವರ್ಹ್ಯಾಂಗಿಂಗ್ ಈವ್ಸ್ . <ref name=":4" /> ''ಮದನಿಕೆರೆನ್'' ಬೇತೆ ಬೇತೆ ಪಾತ್ರೊಲೆಡ್ ಬೊಕ್ಕ ನಲಿಕೆದ ಭಂಗಿಲೆಡ್ ತೋಜಾವೆರ್, ಉಂದೆಟ್ ನಗಿನಿ ಪನ್ಪಿನ ಒರಿ ಸರ್ಪೊನು ತೂಪಿನ ''ಯೋಗಿನಿ'' '','' ''ಮರ್ದಾಲ'' ಪನ್ಪಿನ ಒರಿ ಡ್ರಮ್ಮರ್, ಬಿಲ್ಲು ಬೊಕ್ಕ ಬಾಣೊನು ಹಿಡಿಯೊಂದಿತ್ತಿನ ಒರಿ ಬೇಟೆಗಾರೆ, ''ರತಿ'' ಬೊಕ್ಕ ''ಗಯಾನಿ'' ಪನ್ಪಿನ ''ತ್ರಿಭಂಗ'' ಭಂಗಿಡ್ ನಿಲೆಯಾತಿನ ಒರಿ ಗಾಯಕಿ ಸೇರ್ದ್ ಉಲ್ಲೆರ್. <ref name=":3" /> ಗಜ-ವ್ಯಾಲೊಲು ಆನೆಲೆನ ತರೆತ ಮಿತ್ತ್ ಉಗ್ರ ಅಭಿವ್ಯಕ್ತಿಲೆನ್ ತೋಜಾದ್ ನಿಲೆಯಾದ್ ತೋಜಾವೆರ್. <ref name=":4" /> ''ನಾಟ್ಯ ಮಂಟಪ (ನೃತ್ಯ ಸಭಾಂಗಣ)ದ ನಡುತ ನಾಲ್ ಡೋಲರೈಟ್ ಕಂಬಲೆಡ್ ಸೂಕ್ಷ್ಮವಾಯಿನ ನೇಲುನ ಕೆತ್ತನೆಲು ಉಂಡು.ಐಟ್ ಪುರಾಣ ಕಥೆಕ್ಲೆದ ಎಲ್ಯ ಚಿತ್ರನಲು, ಸರಪಳಿಲು ಬೊಕ್ಕ ಪೂತ ಅಲಂಕಾರ ಬಾರೀ ನಾಜೂಕಾದ್ ಕೆತ್ತಾದೆರ್.'' <ref name=":4" /> ಈ ಚಾವಣಿಡ್ ''ರಾಮಾಯಣ'', ''[[ಮಹಾಭಾರತ]]'', ಬೊಕ್ಕ ''ಭಾಗವತ ಪುರಾಣೊದ'' ವಿವಿಧ ಘಟನೆಲೆನ್ ತೋಜಾವುಂಡು, ಉಂದೆಟ್ ''ಸಮುದ್ರ ಮಂಥನ'', ''ತ್ರಿಪುರಾಸುರನ'' ನಾಶ, ಬೊಕ್ಕ ''[[ಶಿವ]]'' ಬೊಕ್ಕ ''[[ಪಾರ್ವತಿ|ಪಾರ್ವತಿನ]]'' ಮದಿಮೆ ಸೇರ್ದ್ ಮುಖ್ಯ ಆದ್ ಉಂಡು. <ref name=":3" /> ''ಅಂತರಾಲದ ಸಮೀಪೊಡು ದಲಮಾಲಿಕಾ ಪಂದು ಲೆಪ್ಪುನ ವ್ಯಕ್ತಿನ್ ಪ್ರತಿಯೊಂದು ಇನ್ಪುಟ್ ಉಲಾಯಿಡ್ ಟೊಳ್ಳಾದ್ ಕೆತ್ತ್ ನ ಕಲ್ಲ್ ದ ಕೊಂಬೆ‌ಉಂಡು. ಐನ್ ನಿರ್ದಿಷ್ಟ ಜಾಗೆಲೆಡ್ ಬೊಟ್ಟ್ ನಗ ಸಂಗೀತದ ಏಳ್ ಸ್ವರೊಕುಲು ಕೇನುಂಡು.'' <ref name=":4" /> ಒಟ್ಟುಗು, ಈ ದೇವಸ್ಥಾನೊಡು ಸುಮಾರ್ 600 ನಲಿಕೆಗ್ ಸಂಬಂಧಪಟ್ಟಿನ ಶಿಲ್ಪೊಲು ಉಂಡು, ಅವು ಆಜಿ ಇಂಚ್ ರ್ದ್ ಆಜಿ ಅಡಿ ಎತ್ತರೊಡು ಉಂಡು , ''ಉಂದು ಪೆರಿನಿ, ರಾಕಸ, ಪ್ರೇಕ್ಷಣ, ಗೌಂಡಲಿ, ಬೊಕ್ಕ ದಂಡರಾಸಕ ಘಾತಿಶಿಶ್ರೀ ಸೇರ್ದ್ ನಲಿಕೆದ ಸಂಪ್ರದಾಯೊಲೆನ ದೃಶ್ಯ ದಾಖಲೆನ್ ರೂಪಿಸವುಂಡು.'' <ref name=":4" /> ಈ ದೇವಸ್ಥಾನೊಡು ಉಪ್ಪುನ ಶಿಲ್ಪೊಲೆನ್ ತೂದು ನಟರಾಜ ರಾಮಕೃಷ್ಣೆರ್ ಪೆರಿನಿ ಶಿವತಾಂಡವೊನು (ಪೆರಿಣಿ ನೃತ್ಯ) ಪುನರುಜ್ಜೀವನ ಮಲ್ತೆರ್. <ref name=":0">{{Cite web|last=P.|first=Surya Rao|date=10 November 2006|title=Blast from the past|url=http://www.hinduonnet.com/thehindu/fr/2006/11/10/stories/2006111000010200.htm|archiveurl=https://web.archive.org/web/20071111192416/http://www.hinduonnet.com/thehindu/fr/2006/11/10/stories/2006111000010200.htm|archivedate=11 November 2007|website=[[The Hindu]]}}</ref> <ref name=":1">{{Cite book |last=Gupta |first=Roxanne Kamayani |url=https://books.google.com/books?id=DVsoDwAAQBAJ&dq=Perini+dance&pg=PT38 |title=A Yoga of Indian Classical Dance: The Yogini's Mirror |date=2000-03-01 |publisher=Simon and Schuster |isbn=978-1-59477-527-7 |language=en}}</ref> ಜಯಪ ಸೇನಾನಿ ''ನೃತ್ತ ರತ್ನವಳಿಡ್'' ಬರೆತಿನ ನಲಿಕೆದ ಭಂಗಿಲುಲಾ ಈ ಶಿಲ್ಪೊಲೆಡ್ ತೋಜುಂಡು. <ref name=":2">{{Cite book |last=Ponvannan |first=Gayathri |url=https://books.google.com/books?id=z6VXEAAAQBAJ&dq=Perini+dance&pg=PT140 |title=100 Great Chronicles of Indian History: From Cave Paintings to the Constitution |date=2022-01-25 |publisher=Hachette India |isbn=978-93-91028-77-0 |language=en}}</ref> ಈ ದೇಸ್ತಾನೊನು ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಶಿಲ್ಪಿ ರಾಮಪ್ಪನ ಪುದರ್ಡ್ ಈ ದೇಸ್ತಾನೊಗು ಪುದರ್ ಬೈದ್ಂಡ್, ಇಂದೆನ್ ಕುಶಲಕರ್ಮಿನ ಪುದರ್ಡ್ ಭಾರತದ ಒಂಜೇ ಒಂಜಿ ದೇಸ್ತಾನ ಆದ್ ದೀತೆರ್. [ 7 ] == ಕಾಪುನು == ಪ್ರೊಫೆಸರ್ ಗುಲಾಮ್ ಯಜ್ದಾನಿ ನಿರ್ದೇಶನೊಡು ಹೈದರಾಬಾದ್ ದ 7ನೇ ನಿಜಾಮ್ ದ ಪುರಾತತ್ವ ವಿಭಾಗೊದ ಅಡಿಟ್ ''ರುದ್ರೇಶ್ವರ'' ದೇವಸ್ಥಾನೊದ ಔಪಚಾರಿಕ ಸಂರಕ್ಷಣೆ 1914ಡ್ ಸುರು ಆಂಡ್. <ref>{{Cite news|url=https://timesofindia.indiatimes.com/city/hyderabad/ramappa-was-first-renovated-in-1914/articleshow/84744142.cms|title=Ramappa temple’s first renovation effort was taken up in 1914|date=26 July 2021|work=The Times of India}}</ref> ೧೯೩೧ಡ್ ಡಾ.ಗುಲಾಮ್ ಯಜ್ದಾನಿ ಈ ದೇವಸ್ಥಾನೊದ ಕಳೆಂಜಿನ ಅಲಂಕಾರೊಲೆನ್ ಬೊಕ್ಕ ಬಂಗಾರ್ಲೆನ್ ಪತ್ತೆ ಮಲ್ತ್ ದ್ ಅಯಿತ ಮೂಲ ಸ್ಥಾನೊಗು ಪುನರ್ ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್. <ref>{{Cite news|url=https://archive.siasat.com/news/unesco-declared-ramappa-temple-as-world-heritage-site-mentions-nizams-role-in-its-restoration-2170143/|title=UNESCO mentions Nizam's role in restoration of Ramappa Temple|last=Ahmed|first=Mohammed Hussain|date=27 July 2021|work=The Siasat Daily – Archive}}</ref> ೧೯೪೮ಡ್ ಹೈದರಾಬಾದ್ ನ್ ಭಾರತೀಯ ಒಕ್ಕೂಟೊಗು ಸೇರ್ಪಾದ್, ಕಸ್ಟೋಡಿಯನ್ಶಿಪ್ ಬೊಕ್ಕ ನಿರ್ವಹಣೆನ್ ಆರ್ಕಿಯಾಲಜಿಕಲ್ ಸರ್ವೆ ಆಫ್ ಇಂಡಿಯಾ (ಎಎಸ್ಐ) ಗ್ ವರ್ಗಾವಣೆ ಮಲ್ತೆರ್. <ref name=":4" /> ೧೯೯೧ಡ್, ರಾಷ್ಟ್ರೀಯ ತಂತ್ರಜ್ಞಾನ ಸಂಸ್ಥೆ (ಎನ್ಐಟಿ), ವಾರಂಗಲ್ ದೇವಸ್ಥಾನೊದ ಅನನ್ಯ ಎಂಜಿನಿಯರಿಂಗ್ ಬೊಕ್ಕ ರಚನಾತ್ಮಕ ಅಂಶೊಲೆನ್ ತನಿಖೆ ಮಲ್ಪೆರೆ ಭೂತಾಂತ್ರಿಕ ಮೌಲ್ಯಮಾಪನೊನು ನಡಪಾಯೆರ್. <ref name=":4" /> ''ನಂದಿ ಮಂಟಪೊಗು'' ಮಹತ್ವದ ಜೀರ್ಣೋದ್ಧಾರ ಯೋಜನೆಲು ಮುಗಿದ್ಂಡ್, ಆಂಡ ''ಕಾಮೇಶ್ವರ'' ದೇವಸ್ಥಾನೊನು ಮೂಲ ಅಂಶೊಲೆನ್ ಗಲಸ್ದ್ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ತೊಂದುಲ್ಲೆರ್. <ref name=":4" /> ನವೆಂಬರ್ ೨೦೨೪ಡ್, ಭಾರತ ಸರ್ಕಾರ, ಬಂಡವಾಳ ಹೂಡಿಕೆಗ್ ರಾಜ್ಯೊಲೆಗ್ ವಿಶೇಷ ಸಹಾಯಕ (SASCI) ಯೋಜನೆದ ಅಡಿಟ್, ₹ ೭೩ ಕೋಟಿ ನ್ ನಿಗದಿ ಮಲ್ತ್ಂಡ್ ದೇವಸ್ಥಾನದ ಜೀರ್ಣೋದ್ಧಾರ ಬೊಕ್ಕ ಸಂರಕ್ಷಣೆದ ಯೋಜನೆಗಾದ್ ತೆಲಂಗಾಣ ಸರಕಾರೊರ್ದು . <ref>{{Cite web|last=Sana|first=Saicharan|date=23 December 2024|title=Mulugu's 13th century Ramappa temple, a World Heritage Site, has 21st century problems|url=https://thesouthfirst.com/telangana/mulugus-13th-century-ramappa-temple-a-world-heritage-site-has-21st-century-problems/|accessdate=18 July 2026}}</ref> ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ನೈಸರ್ಗಿಕ ಜಾಗೆಲೆನ್ ಸಂರಕ್ಷಣೆ ಮಲ್ಪರೆ, ದೇವಸ್ಥಾನ ಬೊಕ್ಕ ಅಯಿತ ಸುತ್ತಮುತ್ತದ ಪ್ರದೇಶೊನು ಪ್ರಾಚೀನ ಸ್ಮಾರಕೊಲು ಬೊಕ್ಕ ಪುರಾತತ್ತ್ವ ಶಾಸ್ತ್ರದ ತಾಣೊಲು ಬೊಕ್ಕ ಅವಶೇಷೊಲು (ಎಎಂಎಎಸ್ಆರ್) ಕಾಯ್ದೆದ ಅಡಿಟ್ ಒಂಜಿ ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶೊ ಆದ್ ಗೊತ್ತುಪಡಿಸವೊಂಡುಂಡು. ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶೊನು ಮುಖ್ಯ ದೇವಸ್ಥಾನ ಸಂಕೀರ್ಣೊನು ಒಳಗೊಂಡ 5.93-ಹೆಕ್ಟೇರ್ ಕೋರ್ ವಲಯೊಗು ಬೊಕ್ಕ ಕೋರ್ ಗಡಿಡ್ದ್ 300 ಮೀಟರ್ ಮುಟ್ಟ ವಿಸ್ತಾರವಾದ್ 66.27-ಹೆಕ್ಟೇರ್ ಬಫರ್ ವಲಯೊಗು ವಿಂಗಡಿಸವೊಂಡುಂಡು. <ref name=":4" /> == ಜಾಗೆ == ಮುಲುಗು ಜಿಲ್ಲೆದ ವೆಂಕಟಾಪುರ ಮಂಡಲದ ಪಾಲಂಪೆಟ್ ಡ್ ರಾಮಪ್ಪ ದೇವಸ್ಥಾನ ಉಂಡು. <ref>{{Cite web|last=Kumar|first=Puli Sarath|date=19 February 2026|title=ASI Issues Notice on National Status for Ramappa Temple|url=https://www.deccanchronicle.com/southern-states/telangana/asi-issues-notice-on-national-status-for-ramappa-temple-1938514|accessdate=19 July 2026}}</ref> ಈ ಊರು ಸುಮಾರ್ {{Convert|19|km|mi}} ದೂರೊಡು ಉಂಡುಜಿಲ್ಲಾ ಮುಖ್ಯ ಕಚೇರಿ ಆಯಿನ ಮುಲುಗುಡ್ದ್ ಸುಮಾರ್ {{Convert|70|km|mi}} ವಾರಂಗಲ್ ನಗರೊಡ್ದ್. <ref>{{Cite web|date=3 July 2026|title=Ramappa Temple (Rudreshwara Temple)|url=https://www.telangana360.com/2015/12/ramappa-temple.html|accessdate=19 July 2026}}</ref> ಈ ದೇವಸ್ಥಾನದ ಭೌಗೋಳಿಕ ನಿರ್ದೇಶಾಂಕೊಲು 18°15′32.88′′ ಉತ್ತರ, 79°56′35.54′′ ಪೂರ್ವ. <ref name=":4" /> ಕೈತಲ್ದ ರೈಲ್ವೆ ನಿಲ್ದಾಣ ವಾರಂಗಲ್ ಡ್ ಉಂಡು, ಅವು ಸುಮಾರ್ 70ಕಿ.ಮೀ&nbsp;ದೇವಸ್ಥಾನರ್ದ್ ದೂರಡ್ ಉಂಡು. ಸುತ್ತಮುತ್ತಲದ ವಿಮಾನ ನಿಲ್ದಾಣ ಹೈದರಾಬಾದ್ ಡ್ ಉಂಡು ಸುಮಾರ್ 150&nbsp;ಕಿ.ಮೀ ದೂರ. <ref>{{Cite web|date=16 July 2026|title=Ramappa Temple|url=https://tourism.telangana.gov.in/destinations/ramappa|accessdate=16 July 2026}}</ref> == ಗ್ಯಾಲರಿ == <gallery widths="200px" heights="200px"> ಫೈಲ್:Ramappa_Temple_(Human_Scale).jpg|alt=From the side| ಬದಿಡ್ದ್ ಫೈಲ್:Ramappa_Temple_02.jpg ಫೈಲ್:Temple_-_Front_view.JPG|alt=Front| ಎದುರು ಫೈಲ್:Temples_of_India.jpg ಫೈಲ್:A_view_of_Ramappa_Temple_01.JPG|alt=From behind| ಪಿರವುಡ್ದ್ ಫೈಲ್:Ramappa_Temple_by_Varsha_Bhargavi_Kondapalli_21.jpg|alt=One of the distinctive dancers| ಒಂಜಿ ವಿಶಿಷ್ಟ ನರ್ತಕಿ ಫೈಲ್:Ramappa_Temple_by_Varsha_Bhargavi_Kondapalli_38.jpg|alt=Dancer| ನರ್ತಕಿ ಫೈಲ್:Ramappa_Temple_by_Varsha_Bhargavi_Kondapalli_09.jpg|alt=Pillar inside the mandapa| ಮಂಡಪದ ಉಲಯಿ ಕಂಬ . ಫೈಲ್:Ramappa_Temple_by_Varsha_Bhargavi_Kondapalli_14.jpg|alt=Inside the mandapa| ಮಂಡಪದ ಉಲಯಿ . ಫೈಲ್:Ramappa_temple...JPG|alt=Smaller temple|<nowiki> ಎಲ್ಯ ದೇವಸ್ಥಾನ|</nowiki> </gallery> == ಅಂಚನೆ ತೂಲೆ == * ಶ್ರೀ ಲಕ್ಷ್ಮೀ ನರಸಿಂಹ ಸ್ವಾಮಿ ದೇವಸ್ಥಾನ, ಯಡದ್ರಿ == ಉಲ್ಲೇಖೊಲು == <templatestyles src="Reflist/styles.css" />{{Reflist}} === ಗ್ರಂಥಸೂಚಿ ===   *   [[ವರ್ಗೊ:ಪರಿಶೀಲಿಸಂದಿನ ಅನುವಾದೊಲು ಇಪ್ಪುನ ಪುಟೊಕುಲು]] mxjj2rs13wjdvmrj36zshley233hjuu ಸ್ವಿಯಾಟೊಹಿರ್ಸ್ಕ್ ಲಾವ್ರಾ 0 28023 366131 365953 2026-08-21T03:15:03Z InternetArchiveBot 4316 Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5 366131 wikitext text/x-wiki {{Infobox building | name = ಪವಿತ್ರ ಪರ್ವತೊಲೆನ ಪವಿತ್ರ-ಉಸ್ಪೆನ್ಸ್ಕಿ ಲಾವ್ರ | native_name = {{langx|uk|Свято-Успенська Святогірська Лавра}} | native_name_lang = uk | image = Svjatogorsk,_Lavra_3.jpg | image_caption = ಸೆವರ್ಸ್ಕಿ ಡೊನೆಟ್ಸ್ ತುದೆತ ಬಲ ದಂಡೆಡ್ದ್ ದಿಸಾಯಿನ ಸ್ವ್ಯಾಟೊಹಿರ್ಸ್ಕ್ ಲಾವ್ರ | location = ಸ್ವ್ಯಾಟೊಹಿರ್ಸ್ಕ್, ಡೊನೆಟ್ಸ್ಕ್ ಒಬ್ಲಾಸ್ಟ್ | location_country = ಉಕ್ರೇನ್ | coordinates = {{Coord|49|01|41.2|N|37|34|03.2|E|type:landmark|display=inline,title}} | building_type = ಲಾವ್ರ | opened_date = 1526 | owner = ಉಕ್ರೇನಿಯನ್ ಆರ್ಥಡಾಕ್ಸ್ ಚರ್ಚ್ | mapframe = yes | mapframe-zoom = 14 | mapframe-marker = religious-christian }} '''ಪವಿತ್ರ ಪರ್ವತೊಲೆನ ಪವಿತ್ರ ಡಾರ್ಮಿಷನ್ ಲಾವ್ರಾ''' ({{langx|uk|Свято-Успенська Святогірська Лавра}}, '''ಸ್ವಿಯಾಟೊಹಿರ್ಸ್ಕ್ ಲಾವ್ರಾ''' ಅತ್ತಂದೆ '''ಸ್ವಿಯಾಟೊಹಿರ್ಸ್ಕ್ ಗುಹೆ ಮಠ''') ಡೊನೆಟ್ಸ್ ತುದೆತ ಬಲ ದಂಡೆಡ್ ಇಪ್ಪುನ ಒಂಜಿ ಪ್ರಮುಖ ಆರ್ಥೊಡಾಕ್ಸ್ ಕ್ರಿಶ್ಚಿಯನ್ ಮಠ ಅದುಂಡು.<ref>{{cite web |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |title=Sviati Hory Dormition Monastery |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಈ ಪುದರ್ ಸುತ್ತಮುತ್ತದ ಪವಿತ್ರ ಪರ್ವತೊಲುದ ಪುದರ್ದ್ ಬತ್ತಿನಿ, ಬೊಕ್ಕ "ಪವಿತ್ರ ಡಾರ್ಮಿಷನ್" ಪಂಡ ಕನ್ಯೆ ಮೇರಿನ ಸ್ವರ್ಗಾರೋಹಣೊನು ಸೂಚಿಸುನ ಧಾರ್ಮಿಕ ಪುದರ್ ಆದುಂಡು.<ref>{{cite web |url=https://en.wikipedia.org/wiki/Sviatohirsk_Lavra |title=Sviatohirsk Lavra |access-date=20 August 2026 }}</ref> ಈ ಮಠ ಸ್ವಿಯಾಟೊಹಿರ್ಸ್ಕ್ ಊರುದ ಸಮೀಪೊಡು, ಡೊನೆಟ್ಸ್ಕ್ ಒಬ್ಲಾಸ್ಟ್, ಪೂರ್ವ ಉಕ್ರೇನ್‌ಡ್ ಉಂಡು.<ref>{{cite web |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |title=Sviati Hory Dormition Monastery |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಠೊ ಪವಿತ್ರ ಪರ್ವತೊಲೆನ ರಾಷ್ಟ್ರೀಯ ಪ್ರಕೃತಿ ಉದ್ಯಾನವನೊದ ಕೇಂದ್ರ ಭಾಗೊಡು ಉಂಡು. ಈ ಉದ್ಯಾನವನ 1997ಡ್ ಸ್ಥಾಪನೆ ಆತುಂಡು.<ref>{{cite web |url=https://en.wikipedia.org/wiki/Holy_Mountains_National_Nature_Park |title=Holy Mountains National Nature Park |access-date=20 August 2026 }}</ref> ಉಕ್ರೇನಿಯನ್ ಆರ್ಥೊಡಾಕ್ಸ್ ಚರ್ಚ್ 9 ಮಾರ್ಚ್ 2004ಡ್ ಈ ಮಠೊಗು "ಲಾವ್ರಾ" ಸ್ಥಾನಮಾನ ಕೊರ್ತುಂಡು.<ref>{{cite web |url=https://svlavra.church.ua/istoriya/ |title=Історія — Свято-Успенська Святогірська Лавра |publisher=Свято-Успенська Святогірська Лавра |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == ಮಠದ ಸುರುತ ಲಿಖಿತ ಉಲ್ಲೇಖೊ 1624ಡ್ ತಿಕ್ಕುದುಂಡು. ಆಂಡಾ ಆಸ್ಟ್ರಿಯನ್ ರಾಜತಾಂತ್ರಿಕ ಸಿಗಿಸ್ಮಂಡ್ ವಾನ್ ಹರ್ಬರ್ಸ್ಟೈನ್ 1526ಡ್ "ಪವಿತ್ರ ಪರ್ವತೊಲೆನ" ಪ್ರದೇಶೊನು ಉಲ್ಲೇಕ ಮಲ್ತಿತ್ತೆರ್; ಉಂದು ಮಠೊದ ಉಲ್ಲೇಕ ಉಪ್ಪಂದೆ ಕಾಲಿ ಪ್ರದೇಶೊದ ಉಲ್ಲೇಖ ಉಂಡು.<ref>{{cite web |url=https://decentralization.gov.ua/en/news/19733 |title=Sviatohirsk is keeping strong. And so is Ukraine |publisher=Decentralization of Ukraine |access-date=20 August 2026 }}</ref> ಸುರುತ ಸನ್ಯಾಸಿಲು 15ನೇ ಶತಮಾನೊಡು ಡೊನೆಟ್ಸ್ ತುದೆನ್ ತೋಜುನ ಗುಹೆಲೆಡ್ ವಾಸ ಮಲ್ಪುನ ಸಾಧ್ಯತೆ ಉಂಡು ಪನ್ಪಿನ ಇತಿಹಾಸಿಕ ಉಲ್ಲೇಕೊಲು ಉಂಡು. ಮಠೊ ಬೊಕ್ಕ ಕ್ರಿಮಿಯನ್ ಟಾಟಾರ್‌ಲೆನ್ ದಾಳಿಲೆಗ್ ಬಲಿಯಾದ್ ಒಂಜಿ ಗಡಿ ಪ್ರದೇಶದ ಧಾರ್ಮಿಕ ಕೇಂದ್ರ ಆದ್ ಬೆಳದ್ ಬತ್ತುಂಡು.<ref>{{cite web |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |title=Sviati Hory Dormition Monastery |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ೧೭೮೭ಡ್, ರಷ್ಯಾದ ಕ್ಯಾಥರೀನ್ II ಈ ಮಠೊನು ಬಂದ್ ಮಲ್ತೆರ್. ಮಠದ ಜಮೀನುಲೆನ್ ಜಾತ್ಯತೀತೀಕರಣ ಮಲ್ತೆರ್ ಬೊಕ್ಕ ನ್ಯೂ ರಷ್ಯಾದ ವೈಸ್ರಾಯ್ ಆಯಿನ ಪ್ರಿನ್ಸ್ ಗ್ರಿಗರಿ ಪೊಟೆಮ್ಕಿನ್ಗ್ ದಾನ ಮಲ್ತೆರ್.<ref>{{cite web |title=Sviati Hory Dormition Monastery |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಅರೆನ ಉತ್ತರಾಧಿಕಾರಿಲೆಡ್ ಒರಿಯೆ, ಅಲೆಕ್ಸಾಂಡರ್ ಮಿಖೈಲೋವಿಚ್ ಪೊಟೆಮ್ಕಿನ್, ಬೊಕ್ಕ ಅರೆನ ಬೊಡೆದಿ ತಾಟಿಯಾನಾ ಬೊರಿಸೊವ್ನಾ ಪೊಟೆಮ್ಕಿನಾ (ರಾಜಕುಮಾರಿ ಹೌಸ್ ಆಫ್ ಗೋಲಿಟ್ಸಿನ್), 1844ಡ್ ಸುರು ಆಯಿನ ಮಠೊದ ಜೀರ್ಣೋದ್ಧಾರೊಗು ಪುಂಡಿ ಪಣವು ಕೊರ್ತೆರ್.<ref>{{cite web |title=Sviatohirsk Lavra |url=https://en.wikipedia.org/wiki/Sviatohirsk_Lavra |access-date=20 August 2026 }}</ref> === ಸೋವಿಯತ್ ಕಾಲೊಡು === ಅಕ್ಟೋಬರ್ ಕ್ರಾಂತಿಗ್ ದುಂಬು, ಸ್ವಿಯಾಟೊಹಿರ್ಸ್ಕ್ ಮಠೊಡು ಒಂಜಿ ಕಾರ್ಮಿಕೆರೆನ ಅಂಗಡಿ, ಗಾಳಿ ಮಿಲ್ ಲು, ಬೇತೆ ಬೇತೆ ರೀತಿದ ದುರಸ್ತಿ ಅಂಗಡಿಲು ಬೊಕ್ಕ ವ್ಯಾಪಾರ ಕಟ್ಟಡೊಲು ಇತ್ತ್ಂಡ್. ಮಠೊದ ಆರ್ಥಿಕ ಚಟುವಟಿಕೆಲು ಬೊಕ್ಕ ಕಟ್ಟಡೊಲೆನ ಬಗ್ಗೆ ಇತಿಹಾಸಿಕ ದಾಖಲೆಲು ತಿಕ್ಕುದುಂಡು.<ref>{{cite web |title=Sviati Hory Dormition Monastery |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಲಾವ್ರಾದ ಮುಖ್ಯ ಡಾರ್ಮಿಷನ್ ಕ್ಯಾಥೆಡ್ರಲ್‌ನ್ ಅಲೆಕ್ಸೆ ಗೊರ್ನೊಸ್ಟಾವ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್; ಕಟ್ಟಡೊಡು ಬೈಜಾಂಟೈನ್ ವಾಸ್ತುಶಿಲ್ಪ ವಾಸ್ತುಶಿಲ್ಪದ ಪ್ರಭಾವೊ ಇತ್ತ್ಂಡ್.<ref>{{cite web |title=Sviatohirsk Lavra |url=https://en.wikipedia.org/wiki/Sviatohirsk_Lavra |access-date=20 August 2026 }}</ref> ೧೯೧೭ಡ್ ಅಕ್ಟೋಬರ್ ಕ್ರಾಂತಿ ಆದ್ ಬೊಕ್ಕ, ಬೊಲ್ಶೆವಿಕ್ ಆಡಳಿತೊದ ಕಾಲೊಡು ಮಠೊಗು ತೊಂದರೆಲು ಎದುರಾಂಡ್. ಮಠೊದ ಆಸ್ತಿ ವಶಪಡಿಸಾಯೆರ್ ಬೊಕ್ಕ ಧಾರ್ಮಿಕ ಚಟುವಟಿಕೆಲೆಗ್ ನಿರ್ಬಂಧೊಲು ವಿಧಿಸಾಯೆರ್. ೧೯೨೨ಡ್ ಮಠೊನು ಅಧಿಕೃತವಾದ್ ಬಂದ್ ಮಲ್ತೆರ್ ಬೊಕ್ಕ, ಉಂದೆನ್ ಡೊನ್ಬಾಸ್ ಪ್ರದೇಶದ ಕಾರ್ಮಿಕೆರೆಗ್ ಸ್ಯಾನಿಟೋರಿಯಂ ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್.<ref>{{cite web |title=Sviatohirsk Lavra |url=https://en.wikipedia.org/wiki/Sviatohirsk_Lavra |access-date=20 August 2026 }}</ref> ಸುರುತ ವಿಶ್ವ ಯುದ್ಧೊಡ್ದು ದುಂಬು, ಈ ಮಠೊಡು ಅಂದಾಜಿ ೬೦೦ ಸನ್ಯಾಸಿಲು ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್. ೧೯೩೦ ದಶಕೊಡು, ಕೆಲವು ಚರ್ಚ್ ಲೆನ್ ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟ, ಬೇತೆ ಅಸಂಖ್ಯಾತ ಯುಎಸ್ಎಸ್ಆರ್ ಧಾರ್ಮಿಕ ವಿರೋಧಿ ಅಭಿಯಾನ (೧೯೨೮-೧೯೪೧)/ ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟೊಡು ಧಾರ್ಮಿಕ ತಾಣೊಲೆನ್ ಕೆತ್ತುದು ಪಾಡ್ಯೆರ್. === ಸ್ವತಂತ್ರ ಉಕ್ರೇನ್ ಡ್ ಪುನಃಸ್ಥಾಪನೆ === ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟದ ಇತಿಹಾಸ (೧೯೮೫-೧೯೯೧)/ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟದ ಪತನ ಬೊಕ್ಕ ೧೯೯೧ಡ್ ಉಕ್ರೇನ್ ಸ್ವಾತಂತ್ರ್ಯ ಘೋಷಣೆ ಪಡೆತಿನ ಬುಕ್ಕೊ, ೧೯೯೨ಡ್ ಮಠದ ಧಾರ್ಮಿಕ ಚಟುವಟಿಕೆಲೆನ್ ಪುನಃಸ್ಥಾಪನೆ ಮಲ್ತೆರ್.<ref>{{cite web |title=Sviati Hory Dormition Monastery |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ೨೦೦೪ಡ್, ಉಕ್ರೇನಿಯನ್ ಆರ್ಥೊಡಾಕ್ಸ್ ಚರ್ಚ್ (ಮಾಸ್ಕೋ ಪೇಟ್ರಿಯಾರ್ಕೇಟ್) ಮಠೊಗು ಅಧಿಕೃತವಾದ್ "ಲಾವ್ರಾ" ಸ್ಥಾನಮಾನೊನು ಕೊರ್ತುಂಡು.<ref>{{cite web |title=Історія |url=https://svlavra.church.ua/istoriya/ |publisher=Свято-Успенська Святогірська Лавра |access-date=20 August 2026 }}</ref> ಇತ್ತೆ, ಮಠದ ಸಮುದಾಯೊಡು 100 ಡ್ದ್ ಜಾಸ್ತಿ ಸನ್ಯಾಸಿಲು ಉಲ್ಲೆರ್.<ref>{{cite web |title=Sviatohirsk Lavra |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CS%5CV%5CSviatiHoryDormitionMonastery.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಅಕ್ಟೋಬರ್ ೨೫, ೨೦೦೫ಡ್, ನ್ಯಾಷನಲ್ ಬ್ಯಾಂಕ್ ಆಫ್ ಉಕ್ರೇನ್ ಸ್ವಿಯಾಟೊಹಿರ್ಸ್ಕ್ ಲಾವ್ರಾನ್ ಚಿತ್ರಿಸವುನ ೧೦-ಉಕ್ರೇನಿಯನ್ ಹೈವ್ನಿಯಾ ಸ್ಮರಣಾರ್ಥ ನಾಣ್ಯೊನು ಬಿಡುಗಡೆ ಮಲ್ತ್ಂಡ್.<ref>{{cite web|url=http://ua.for-ua.com/ukraine/2005/10/18/180124.html |archive-url=https://web.archive.org/web/20070929120701/http://ua.for-ua.com/ukraine/2005/10/18/180124.html |url-status=dead |archive-date=2007-09-29 |title=NBU releases the Sviatohirsk Lavra on the "chervonets" |access-date=2007-04-27 |work=ForUm |language=Russian }}</ref> === 2022 ರಷ್ಯಾದ ಶೆಲ್ಲಿಂಗ್ === [[File:St. George's Skete of Sviatohirsk Lavra after Russian shelling (01).jpg|200px|thumb|ಶೆಲ್ಲಿಂಗ್ ದ ಬೊಕ್ಕ ಲಾವ್ರಾದ ಸೈಂಟ್ ಜಾರ್ಜ್ ಸ್ಕೆಟ್, ಮೇ ೨೦೨೨]] ಉಕ್ರೇನ್ ದ ಮಿತ್ತ್ ರಷ್ಯಾದ ಆಕ್ರಮಣೊದ ಪೊರ್ತುಡು, ಮಠ ನಾಗರಿಕ ನಿರಾಶ್ರಿತೆರೆಗ್ ಆಶ್ರಯೊನು ಕೊರುಂಡು. ಮಾರ್ಚ್ ೧೨, ೨೦೨೨ಡ್, ಡೊನೆಟ್ಸ್ ದಾಂಟೊಂದು ಇತ್ತೆದ ಕೈತಲ್ದ ಸೇತುವೆಡ್ ರಷ್ಯಾದ ವಾಯುದಾಳಿಡ್ದ್ ಉಂದು ಹಾನಿಯಾತ್ಂಡ್, ಆ ಸಮಯೊಡು ಸುಮಾರ್ ೫೨೦ ನಿರಾಶ್ರಿತೆರ್ ಮಠೊಡು ಇತ್ತೆರ್. ಸ್ಫೋಟೊಡ್ದ್ ಬತ್ತಿನ ಸ್ಫೋಟೊಡು ಮಠದ ಕಟ್ಟಡೊಲೆನ ಕಂಡಿಲು ಬೊಕ್ಕ ಬಾಕಿಲುಲು ಹಾನಿ ಆಂಡ್, ಬೊಕ್ಕ ಪುಡಿಪುನ ಕಂಡಿಲೆನ ಗಾಜಿದ ತುಂಡುಲೆಡ್ದ್ ಕೆಲವು ಜನ ಗಾಯೊ ಆದು ಇತ್ತುಂಡು.<ref>[https://svlavra.church.ua/2022/03/13/aviaudar-po-svyatogorskoj-lavre/ Авиаудар по Святогорской Лавре (svlavra.church.ua)], 13 March 2022. {{cite news |title=Russian airstrike damages historic Ukrainian monastery |url=https://edition.cnn.com/europe/live-news/ukraine-russia-putin-news-03-13-22#h_e1905e260badeb635420976a05d72825 |access-date=13 March 2022 |agency=CNN |date=13 March 2022 |language=en}}</ref>ಮೇ ೪ ತಾರೀಕ್ ಗ್, ಪುನರಾವರ್ತಿತ ಗುಂಡು ದಾಳಿಡ್ ೭ ಜನ ಗಾಯೊ ಆಂಡ್.<ref>{{cite web |url=https://www.forbes.com/sites/katyasoldak/2022/05/05/thursday-may-5-russias-war-on-ukraine-news-and-information-from-ukraine/?sh=6d4ebb85e1ff |title=Thursday, May 5. Russia's War On Ukraine: News And Information From Ukraine |date=2022-05-05 |work=Forbes }}</ref> ಮೇ ಡ್ ನನೊಂಜಿ ಸರ್ತಿ, ರಷ್ಯಾದ ಶೆಲ್ಲಿಂಗ್ ಅವರ್ ಲೇಡಿ ಆಫ್ ದ ಜಾಯ್ ಆಫ್ ಆಲ್ ಹು ಸೊರೊ ಸ್ಕೆಟೆ ನ್ ನಾಶ ಮಲ್ತ್ಂಡ್,<ref>{{cite web |url=https://24tv.ua/ru/russkie-obstreljali-hram-bogorodichnom-doneckoj-oblasti-podvale_n1985751 |title=Россияне обстреляли храм в Богородичном Донецкой области: в подвале прятались монахи и дети |date=2022-05-21 |publisher=[[24 Kanal]] |language=ru }}</ref> ಸೈಂಟ್ ಜಾರ್ಜ್ ಸ್ಕೆಟೆ, ಶಾಂಘೈ ಸ್ಕೆಟೆದ ಸೈಂಟ್ ಜಾನ್ ಬೊಕ್ಕ ಲಾವ್ರಾದ ಪವಿತ್ರ ಹುತಾತ್ಮೆರೆನ ಚರ್ಚ್ ಗ್ ಹಾನಿಯಾತ್ಂಡ್.<ref>{{cite web |url=https://thetimeshub.in/the-invaders-destroyed-the-georgievsky-skete-of-the-svyatogorsk-lavra/2843/ |title=The invaders destroyed the Georgievsky Skete of the Svyatogorsk Lavra |date=2022-05-09 |publisher=The Times Hub |accessdate=2026-08-20 |archivedate=2022-06-11 |archive-url=https://web.archive.org/web/20220611051042/https://thetimeshub.in/the-invaders-destroyed-the-georgievsky-skete-of-the-svyatogorsk-lavra/2843/ |url-status=dead }}</ref><ref>{{cite web |url=https://www.svoboda.org/a/hronika-voiny-v-ukraine/31715636/lbl0lbi286341.html |title=Хроника войны в Украине |date=2022-05-08 |publisher=[[Radio Free Europe/Radio Liberty]] |archiveurl=https://web.archive.org/web/20220512191309/https://www.svoboda.org/a/hronika-voiny-v-ukraine/31715636/lbl0lbi286341.html |archivedate=2022-05-12 |language=ru |url-status=live}}</ref><ref>{{cite web |url=https://korrespondent.net/ukraine/4476696-voiska-rf-snova-strelialy-po-skytam-sviatohorskoi-lavry-est-ranenyi |title=Войска РФ снова стреляли по скитам Святогорской лавры: есть раненый |date=2022-05-10 |publisher=[[Korrespondent]] |language=ru }}</ref><ref name=bellingcat_2022_06_07>{{cite web |url=https://www.bellingcat.com/news/uk-and-europe/2022/06/07/clues-to-the-fate-of-five-damaged-cultural-heritage-sites-in-ukraine/ |title=Clues to the Fate of Five Damaged Cultural Heritage Sites in Ukraine |date=2022-06-07 |publisher=[[Bellingcat]] |archiveurl=https://web.archive.org/web/20220609144309/https://www.bellingcat.com/news/uk-and-europe/2022/06/07/clues-to-the-fate-of-five-damaged-cultural-heritage-sites-in-ukraine/ |archivedate=2022-06-09 |url-status=live}}</ref> ಮೇ ೩೦ ತಾರೀಕ್ ಗ್, ಶೆಲ್ಲಿಂಗ್ ಡ್ ರಡ್ಡ್ ಸನ್ಯಾಸಿಲು ಬೊಕ್ಕ ಒರಿ ಸನ್ಯಾಸಿನಿನ ಸಾವು ಆದು, ಮೂಜಿ ಸನ್ಯಾಸಿಲು ಗಾಯ ಆಯೆರ್, ಬೊಕ್ಕ ಆಸ್ತಿಗ್ ಹಾನಿ ಆಂಡ್.<ref name="raskolam">{{cite web|url=https://raskolam.net/en/53577-vid-bombarduvan-zaginuli-ta-poraneni-monahi-svyatogirskoi-lavri-upc|title=The monks of the Sviatohirsk Lavra of the UOC were killed and wounded while the bombing|website=Raskolam.net|access-date=2022-06-04}}</ref><ref>{{Cite web |title=Russian troops kill 7 civilians in Donetsk Oblast, including 3 in a monastery |url=https://news.yahoo.com/russian-troops-kill-7-civilians-185159435.html |access-date=2022-06-02 |website=news.yahoo.com |language=en-US}}</ref> ಜೂನ್ ೪ ತಾರೀಕ್ ಗ್, ಪವಿತ್ರ ಡಾರ್ಮಿಷನ್ ಲಾವ್ರಾದ ಮೈದಾನೊಡು ಮಲ್ಲ ಪ್ರಮಾಣೊಡು ಬೆಂಕಿ ಸುರು ಆಂಡ್, ಬೊಕ್ಕ ಜ್ವಾಲೆ ಮಠೊದ ಮುಖ್ಯ ದೇಗುಲೊನು ಮುಚ್ಚುಂಡು; ಬೆಂಕಿ ಸುರು ಮಲ್ತಿನೆಕ್ ಉಕ್ರೇನಿಯನ್ ಲು ರಷ್ಯಾದ ಸೈನ್ಯೊಲೆನ್ ದೂಷಣೆ ಮಲ್ತೆರ್.<ref>{{Cite web |last=Balevic |first=Katie |title=A temple in Ukraine's Sviatohirsk Lavra monastery is on fire after Russian attack, Zelenskyy adviser says |url=https://www.businessinsider.com/ukraine-temple-sviatohirsk-lavra-on-fire-after-russian-attack-2022-6 |access-date=2022-06-14 |website=Business Insider |language=en-US}}</ref> ಬೆಂಕಿ ಸ್ಕೆಟ್ ಆಫ್ ಆಲ್ ಸೈಂಟ್ಸ್ ನ್ ನಾಶ ಮಲ್ತ್ಂಡ್ ಪನ್ಪಿನ ವರದಿ ಬತ್ತ್ಂಡ್.<ref name="skete all saints destroyed">{{cite news |title=US supports international inquiry into war crimes in Ukraine – as it happened |url=https://www.theguardian.com/world/live/2022/jun/04/russia-ukraine-war-latest-ukrainians-fight-back-sievierodonetsk-putin-macron-africans-victims-food-shortages-zelenskiy-eu?page=with:block-629b43a68f08c28a9854c793#block-629b43a68f08c28a9854c793 |work=The Guardian |date=4 June 2022 |quote="A church in Donetsk has been hit by Russian shelling and has caught fire, with footage and photos circulating of the aftermath."}}</ref> == ಚಿತ್ರೊಲು == <gallery mode="packed" heights="200"> Svjatogorsk, Lavra 14.jpg|ಲಾವ್ರಾದ ಸ್ವಿಯಾಟೊ-ಪೊಕ್ರೊವ್ಸ್ಕಾ ಚರ್ಚ್ Северский Донец и Святогорская Лавра. 2006.jpg|ಬಲತ್ತ ಕಡೆಟ್ ಉಪ್ಪುನ ಸ್ವಿಯಾಟೊ-ಪೊಕ್ರೊವ್ಸ್ಕಾ ಚರ್ಚ್ Деревянная церковь Всех Святых Всехсвятского скита.JPG|ದಿ ಸ್ಕೆಟ್ ಆಫ್ ಆಲ್ ಸೈಂಟ್ಸ್, ಲಾವ್ರಾ ಉಲಾಯಿ ಒಂಜಿ ಆಲ್-ವುಡ್ ಚರ್ಚ್, ೨೦೧೦ಡ್. </gallery> ==ಉಂದುಲಾ ತೂಲೆ== *ಸೋವಿಯೆತ್ ಒಕ್ಕೂಟೊಡು ಕ್ರೈಸ್ತೆರೆನ ಮಿತ್ತ್ ದೌರ್ಜನ್ಯ *ಯುಎಸ್ಎಸ್ಆರ್ ಧರ್ಮ ವಿರೋಧಿ ಅಭಿಯಾನ (೧೯೧೭-೧೯೨೧) *ಯುಎಸ್ಎಸ್ಆರ್ ಧರ್ಮ ವಿರೋಧಿ ಅಭಿಯಾನ (೧೯೨೧-೧೯೨೮) *ಯುಎಸ್ಎಸ್ಆರ್ ಧರ್ಮ ವಿರೋಧಿ ಅಭಿಯಾನ (೧೯೨೮-೧೯೪೧) *ಯುಎಸ್ಎಸ್ಆರ್ ಧರ್ಮ ವಿರೋಧಿ ಅಭಿಯಾನ (೧೯೫೮-೧೯೬೪) *ಯುಎಸ್ಎಸ್ಆರ್ ಧರ್ಮ ವಿರೋಧಿ ಅಭಿಯಾನ (೧೯೭೦-೧೯೯೦) == ಉಲ್ಲೇಕೊಲು == {{Reflist}} * {{cite web|url=http://ua.vlasenko.net/_pgs/pgs-html/pgs2-138.html|title=Sviatogorsky Uspensky Monastyr 17th-19th centuries|accessdate=2007-04-26|last=Vlasenko|first=Petro|work=Monuments of architecture of Ukrainian SSR|pages=138|language=Russian}} * {{cite web|url=http://archiv.orthodox.org.ua/page-1418.html|title= Sviato-Uspenska Sviatohirska Lavra|accessdate=2007-04-26|work=[[Ukrainian Orthodox Church (Moscow Patriarchate)|Ukrainian Orthodox Church]]|language=Ukrainian}} == ಬಾಹ್ಯ ಕೊಂಡಿಲು == {{Commons category|Sviatohirsk Lavra}} * [http://svlavra.church.ua/ Official website] * [http://www.kingofmarble-shmatko.com/engver/w79z.html Svyatogorskaya Blessed Virgin (photo)] [[ವರ್ಗೊ:ಉಕ್ರೇನಿಯನ್ ಆರ್ಥೊಡಾಕ್ಸ್ ಚರ್ಚ್ ದ ಮಠೊಲು (ಮಾಸ್ಕೋ ಪಿತೃಪ್ರಧಾನ)]] [[ವರ್ಗೊ:ಡೊನೆಟ್ಸ್ಕ್ ಒಬ್ಲಾಸ್ಟ್ ಡ್ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[ವರ್ಗೊ:ಧಾರ್ಮಿಕ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು ೧೯೯೨ಡ್ ಮುಗಿತ್ಂಡ್]] [[ವರ್ಗೊ:ಯುರೋಪ್ ಡ್ 1624 ಸ್ಥಾಪನೆಲು]] [[ವರ್ಗೊ:೧೬೨೦ಡ್ ಸ್ಥಾಪನೆ ಆಯಿನ ಧಾರ್ಮಿಕ ಸಂಘಟನೆಲು]] [[ವರ್ಗೊ:ಡೊನೆಟ್ಸ್]] [[ವರ್ಗೊ:ಉಕ್ರೇನ್ ದ ಮಿತ್ತ್ ರಷ್ಯಾದ ಆಕ್ರಮಣದ ಪೊರ್ತುಡು ನಾಶ ಆಯಿನ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[ವರ್ಗೊ:ಲಾವ್ರಸ್]] lc9kbll90x5h6445xpncnme205qgm6q ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್ ಸರಣಿ 0 28026 365975 2026-08-20T12:08:20Z Mahaveer Indra 1023 ಪೊಸ ಬರವು 365975 wikitext text/x-wiki ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. 5hl2eres8yobof3rgc0k708ehj5hqqr 365976 365975 2026-08-20T12:09:05Z Mahaveer Indra 1023 /* */ 365976 wikitext text/x-wiki '''ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್''' ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. 4xjl7wl2asuwmuyofz9ubvmudb8av19 365979 365976 2026-08-20T12:12:31Z Mahaveer Indra 1023 Mahaveer Indra, ಪುಟೊ [[ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿ]] ನ್ [[ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್ ಸರಣಿ]] ಗ್ ಕಡಪುಡಿಯೆರ್: ಸರಿಯಾಯಿನ ಅಧೀಕೃತ ಪುದರ್ 365976 wikitext text/x-wiki '''ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್''' ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. 4xjl7wl2asuwmuyofz9ubvmudb8av19 365981 365979 2026-08-20T12:13:14Z Mahaveer Indra 1023 /* */ 365981 wikitext text/x-wiki '''ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್''' ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. ms3wdaf33zwiru83fpethrdc1s1p70f 365984 365981 2026-08-20T12:15:20Z Mahaveer Indra 1023 /* */ 365984 wikitext text/x-wiki '''ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್''' ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. ==ಚಿತ್ರೊಲು== ==ಪಾತ್ರವರ್ಗೊ== ==ವಿಶ್ಲೇಷಣೆ== ==ಉಲ್ಲೇಕ== 1jo7md4hczrcw2v88xtzz3zx6vjte9i 366158 365984 2026-08-21T08:59:44Z Mahaveer Indra 1023 Template added and translated 366158 wikitext text/x-wiki {{Infobox film | name = ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್ | image = Mission Impossible - film logo.svg | caption = ಸರಣಿದ ಲೋಗೊ | story = | based_on = ಬ್ರೂಸ್ ಗೇಲರ್ ನಿರ್ದೇಶಿಸಾದಿನ ಉಂದುವೇ ಪುದರ್‌ದ ದಾರಾವಾಹಿ | director = <!-- [[WP:INFOBOX]] avoid links within the same page, not a table of contents --> | producer = {{bulleted list|ಟಾಮ್ ಕ್ರೂಸ್ (ಮಾತಾ ಸಿನೆಮಾಡ್)|ಪೌಲಾ ವಾಗ್ನರ್ (೧ಡ್ದ್ ೩ನೇ ಸಿನೆಮಾ ಮುಟ್ಟ)|ಜೆ. ಜೆ. ಅಬ್ರಾಮ್ಸ್ (೪ಡ್ದ್ ೬ನೇ ಸಿನೆಮಾ ಮುಟ್ಟ)|ಕ್ರಿಸ್ಟೋಫರ್ ಮೆಕ್‌ಕ್ವೆರೀ (೬ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ ಮುಟ್ಟ)}} | writer = <!-- writers for the films written, too many to list here --> | starring = {{bulleted list|ಟಾಮ್ ಕ್ರೂಸ್ (ನಾಯಕೆ)|ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್|ಹೆನ್ರಿ ಝೆರ್ನಿ (೧ನೇ, ೭ನೇ ಬುಕ್ಕ ೮ನೇ ಸಿನೆಮಾ)|ಸೈಮನ್ ಪೆಗ್ಗ್ (೩ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ)|ರೆಬೆಕಾ ಫರ್ಗ್ಯುಸನ್ (೫ಡ್ದ್ ೭ನೇ ಸಿನೆಮಾ)}}<!-- Only actors appearing in three or more films --> | narrator = | music = | cinematography = | studio = {{bulleted list|ಪ್ಯಾರಾಮೌಂಟ್ ಪಿಕ್ಚರ್ಸ್| ಕ್ರೂಸ್-ವಾಗ್ನರ್ ನಿರ್ಮಾಣೊ (೧ಡ್ದ್ ೩ನೇ ಸಿನೆಮಾ)|ಸ್ಕೈಡಾನ್ಸ್ (೪ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ){{efn|Known as '''Skydance Productions''' for the fourth and fifth films. The company later merged with [[Paramount Global]] to form Paramount Skydance Corporation.}}|ಟಿಸಿ ಪ್ರೊಡಕ್ಷನ್ಸ್ (೪ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ)|ಬ್ಯಾಡ್ ರೋಬೋಟ್ (೪ಡ್ದ್ ೬ನೇ ಸಿನೆಮಾ)}}<!-- Only film companies producing in three or more films --> | distributor = ಪ್ಯಾರಾಮೌಂಟ್ ಪಿಕ್ಚರ್ಸ್ | runtime = ಅಂದಾಜಿ ೧೭೦ ನಿಮಿಷ | country = ಅಮೇರಿಕಾ | language = ಇಂಗ್ಲಿಷ್ | budget = ₹ ೧೪, ೩೫೬ ಕೋಟಿ <br>($1.5 ಬಿಲಿಯನ್) (ಒಟ್ಟು ೮ ಸಿನೆಮಾ) | gross = ₹ ೪೫,೩೭೨.೮೨ ಕೋಟಿ <br>($4.74 ಬಿಲಿಯನ್) (ಒಟ್ಟು ೮ ಸಿನೆಮಾ) }} '''ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್''' ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. ==ಚಿತ್ರೊಲು== ==ಪಾತ್ರವರ್ಗೊ== ==ವಿಶ್ಲೇಷಣೆ== ==ಉಲ್ಲೇಕ== 8j2z0xu5mbhoso3l89jd260tm3iiu3v 366162 366158 2026-08-21T09:16:46Z Mahaveer Indra 1023 366162 wikitext text/x-wiki {{under construction}} {{Infobox film | name = ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್ | image = Mission Impossible - film logo.svg | caption = ಸರಣಿದ ಲೋಗೊ | story = | based_on = ಬ್ರೂಸ್ ಗೇಲರ್ ನಿರ್ದೇಶಿಸಾದಿನ ಉಂದುವೇ ಪುದರ್‌ದ ದಾರಾವಾಹಿ | director = <!-- [[WP:INFOBOX]] avoid links within the same page, not a table of contents --> | producer = {{bulleted list|ಟಾಮ್ ಕ್ರೂಸ್ (ಮಾತಾ ಸಿನೆಮಾಡ್)|ಪೌಲಾ ವಾಗ್ನರ್ (೧ಡ್ದ್ ೩ನೇ ಸಿನೆಮಾ ಮುಟ್ಟ)|ಜೆ. ಜೆ. ಅಬ್ರಾಮ್ಸ್ (೪ಡ್ದ್ ೬ನೇ ಸಿನೆಮಾ ಮುಟ್ಟ)|ಕ್ರಿಸ್ಟೋಫರ್ ಮೆಕ್‌ಕ್ವೆರೀ (೬ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ ಮುಟ್ಟ)}} | writer = <!-- writers for the films written, too many to list here --> | starring = {{bulleted list|ಟಾಮ್ ಕ್ರೂಸ್ (ನಾಯಕೆ)|ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್|ಹೆನ್ರಿ ಝೆರ್ನಿ (೧ನೇ, ೭ನೇ ಬುಕ್ಕ ೮ನೇ ಸಿನೆಮಾ)|ಸೈಮನ್ ಪೆಗ್ಗ್ (೩ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ)|ರೆಬೆಕಾ ಫರ್ಗ್ಯುಸನ್ (೫ಡ್ದ್ ೭ನೇ ಸಿನೆಮಾ)}}<!-- Only actors appearing in three or more films --> | narrator = | music = | cinematography = | studio = {{bulleted list|ಪ್ಯಾರಾಮೌಂಟ್ ಪಿಕ್ಚರ್ಸ್| ಕ್ರೂಸ್-ವಾಗ್ನರ್ ನಿರ್ಮಾಣೊ (೧ಡ್ದ್ ೩ನೇ ಸಿನೆಮಾ)|ಸ್ಕೈಡಾನ್ಸ್ (೪ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ){{efn|Known as '''Skydance Productions''' for the fourth and fifth films. The company later merged with [[Paramount Global]] to form Paramount Skydance Corporation.}}|ಟಿಸಿ ಪ್ರೊಡಕ್ಷನ್ಸ್ (೪ಡ್ದ್ ೮ನೇ ಸಿನೆಮಾ)|ಬ್ಯಾಡ್ ರೋಬೋಟ್ (೪ಡ್ದ್ ೬ನೇ ಸಿನೆಮಾ)}}<!-- Only film companies producing in three or more films --> | distributor = ಪ್ಯಾರಾಮೌಂಟ್ ಪಿಕ್ಚರ್ಸ್ | runtime = ಅಂದಾಜಿ ೧೭೦ ನಿಮಿಷ | country = ಅಮೇರಿಕಾ | language = ಇಂಗ್ಲಿಷ್ | budget = ₹ ೧೪, ೩೫೬ ಕೋಟಿ <br>($1.5 ಬಿಲಿಯನ್) (ಒಟ್ಟು ೮ ಸಿನೆಮಾ) | gross = ₹ ೪೫,೩೭೨.೮೨ ಕೋಟಿ <br>($4.74 ಬಿಲಿಯನ್) (ಒಟ್ಟು ೮ ಸಿನೆಮಾ) }} '''ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್''' ಉಂದು ಇಂಗ್ಲಿಷ್ ಬಾಸೆದ ಸಿನೆಮಾ ಸರಣಿಲು ಆದುಂಡು. ಸಾಹಸ, ಪತ್ತೆದಾರಿ, ಮಿಲಿಟರಿ ಇಸಯ ಆದಾರಿತವಾಯಿನ ಈ ಸಿನೆಮಾ ಸರಣಿ ಇಂಗ್ಲಿಷ್ ಬಾಸೆಡ್ ಉಂಡು. ೧೯೬೬ನೇ ಇಸವಿಡ್ ಉಂದುವೇ ಪುದರ್‌ದ ಟಿವಿ ದಾರಾವಾಹಿನ್ ಆದರಿಸಾದ್ ಈ ಸಿನೆಮಾ ಬತ್ತ್ಂಡ್. ಇಂದೆಟ್ ಇಡೆ ಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ್ಂಡ್. ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್ ಉಂದೊಂಜಿ ಅಮೇರಿಕಾದ ರಹಸ್ಯ ಮಿಲಿಟರಿ ಸಂಗಟನೆ. ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ಒವ್ವೆ ಬೇಲೆ ಮಲ್ತ್ಂಡಲಾ ಅವು ರೆಕಾರ್ಡ್ ಆಪುಜಿ ಬುಕ್ಕ ಅಮೇರಿಕಾದ ಸರ್ಕಾರ ಜಬದಾರಿ ದೆತೊನುಜಿ. ದೇಸವಿರೋದೀ ಜನೊಕ್ಲೆಡ್ದ್ ಅತ್ಂಡ ಸಂಗಟನೆಲೆಡ್ದ್ ದೇಸೊಗು ತೊಂದರೆ ಬರ್ಪುಂಡು ಪನ್ನಗ ಈ ಸಂಗಟನೆದ ಸದಸ್ಯೆರ್ ರಂಗೊಗು ಜಪ್ಪುವೆರ್, ಉಂದೊಂಜಿ ಕಾಲ್ಪನಿಕ ಸಂಗಟನೆ. ನಾಯಕೆ ಈಥನ್ ಮ್ಯಾಥ್ಯೂ ಹಂಟ್, ಇಂಪಾಸಿಬಲ್ ಮಿಷನ್ ಫೋರ್ಸ್‌ದ ಒರಿ ಸದಸ್ಯೆ, ಅತ್ತಂದೆ ಎಡ್ಡೆ ತರಬೇತಿ ಪಡೆಯಿನ ಒರಿ ಮುಖ್ಯ ಫೀಲ್ಡ್ ಆಪರೇಟಿವ್. ಇಂಬ್ಯನೊಟ್ಟುಗು ತಾಂತ್ರಿಕ ಸಕಾಯೊಗು ಲುಥರ್ ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಬೆಂಜಿ ಡನ್ನ್ ಇಪ್ಪುವೆರ್. ಮಿಷನ್ ಫೋರ್ಸ್‌ದಕುಲು ದೇಸೊಗು ತೊಂದರೆ ಅಪಾಯ ಬನ್ನಗ ಹಂಟ್ ಬುಕ್ಕ ಆಯನೊಟ್ಟುಗು ಇಪ್ಪುನಕ್ಲೆಗ್ ತೆರಿಪಾವೆರ್. ಹಂಟ್, ಸ್ಟಿಕೆಲ್ ಬುಕ್ಕ ಡನ್ನ್ ಮೊಕ್ಲು ತನ್ಕುಲೆ ಬಿರ್ಸಾತಿಕೆ, ಬಲನ್ ಗಲಸ್‌ದ್ ದೇಸೊಗು ಬರ್ಪಿನ ಅಪಾಯೊನು ದೂರ ಮಲ್ಪುವೆರ್. ಸಿನೆಮಾದ ನಾಯಕೆ ಟಾಮ್ ಕ್ರೂಸ್, ಈಥನ್ ಹಂಟ್‌ನ ಪಾತ್ರೊಡು, ಇರ್ವಿಂಗ್ ರಾಮ್ಸೆಸ್ ಲೂಥರ್ ಸ್ಟಿಕೆಲ್‌ನ ಪಾತ್ರೊಡು ಅಂಚನೆ ಸಿಮೊನ್ ಪೆಗ್ ಬೆಂಜಿ ಡನ್ನ್ ಪಾತ್ರೊಡು ಉಲ್ಲೆರ್. ಉಂದು ಅತ್ತಂದೆ ರೆಬೆಕ್ಕಾ ಫರ್ಗುಸನ್, ಜೆರೆಮಿ ರೆನ್ನರ್ ಮೊಕ್ಲುಲಾ ಪಾತ್ರವರ್ಗೊಡು ಉಲ್ಲೆರ್. ಈ ಸರಣಿ ಸಿನೆಮಾಡ್ ಇಡೆಮುಟ್ಟ ಒಟ್ಟು ೮ ಸಿನೆಮಾಲು ಬುಡ್ಕಡೆ ಆತ. ೧೯೯೬ನೇ ಇಸವಿಡ್ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿದ ಸುರುತ ಸಿನೆಮಾ ಬುಡ್ಕಡೆ ಆಂಡ್. ಅಂಚನೆ ಇಂಚಪದ- ಪಂಡ ೮ನೇ ಸಿನೆಮಾ ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್-ಫೈನಲ್ ರೆಕನಿಂಗ್ ೨೦೨೫ನೇ ಮೇ ತಿಂಗೊಲ್ಡು ಬುಡ್ಕಡೆ ಆಂಡ್. ==ಚಿತ್ರೊಲು== ==ಪಾತ್ರವರ್ಗೊ== ==ವಿಶ್ಲೇಷಣೆ== ==ಉಲ್ಲೇಕ== jmmh3sx5sdmm7apqh12x538zqeitvo3 ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿ 0 28027 365980 2026-08-20T12:12:32Z Mahaveer Indra 1023 Mahaveer Indra, ಪುಟೊ [[ಮಿಷನ್ ಇಂಪಾಸಿಬಲ್ ಸರಣಿ]] ನ್ [[ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್ ಸರಣಿ]] ಗ್ ಕಡಪುಡಿಯೆರ್: ಸರಿಯಾಯಿನ ಅಧೀಕೃತ ಪುದರ್ 365980 wikitext text/x-wiki #REDIRECT [[ಮಿಷನ್:ಇಂಪಾಸಿಬಲ್ ಸರಣಿ]] rzdom95uxowhz4p1p2n392yru89zrbv ಟೆಂಪ್ಲೇಟ್:Uw-blank1 10 28028 366082 2026-05-29T09:04:15Z en>Toadspike 0 reduce amount of text and turn into bulleted list to improve readability and information uptake 366082 wikitext text/x-wiki {{{icon|[[File:Information.svg|25px|alt=Information icon]]}}} Hello, I'm <includeonly>[[User:{{safesub<noinclude></noinclude>st:REVISIONUSER}}|{{safesub<noinclude></noinclude>st:REVISIONUSER}}]]</includeonly><noinclude>[[User:Example|Example]]</noinclude>. I noticed that you recently removed all content from {{<includeonly>safesubst:</includeonly>#if:{{{section|}}}|a section at}} {{<includeonly>safesubst:</includeonly>#if:{{{1|}}}|[[:{{{1}}}]]|a page}}. [[WP:No blank pages|Please do not do this]], as blank pages can confuse readers. * If you discover a duplicate article, please [[Wikipedia:Redirect|redirect]] it to another appropriate page. * If a page has been [[Wikipedia:How to spot vandalism|vandalised]], please [[Wikipedia:How to revert a page to an earlier version|revert]] it to the last good version. * If page content has issues, please improve it by [[Wikipedia:How to edit a page|editing]] the page. * If you believe a page should be deleted, please see the [[WP:BEFORE|deletion process]]. * If you wish to experiment, please use {{safesubst:<noinclude/>sandbox link}}. If this was a mistake, don't worry; the removed content has been restored. If you think I made a mistake or if you have any questions, you can leave a message on [[User talk:<includeonly>{{safesub<noinclude></noinclude>st:REVISIONUSER}}</includeonly><noinclude>Jimbo Wales</noinclude>|my talk page]]. {{{2|Thanks.}}}<!-- Template:uw-blank1 --><noinclude> {{Templatesnotice|series=uw-blank|max=2|escalate=yes|escalate_to=delete|extra_usage= * You can specify that only a section was blanked by adding {{para|section|y}} (or {{para|section|yes}}, etc.). }} </noinclude> 4e7r5iwkesl8b1eaf953kqynfpkclrx 366083 366082 2026-08-20T12:59:42Z ChiK 1136 ೧ revisions imported from [[:en:Template:Uw-blank1]] 366082 wikitext text/x-wiki {{{icon|[[File:Information.svg|25px|alt=Information icon]]}}} Hello, I'm <includeonly>[[User:{{safesub<noinclude></noinclude>st:REVISIONUSER}}|{{safesub<noinclude></noinclude>st:REVISIONUSER}}]]</includeonly><noinclude>[[User:Example|Example]]</noinclude>. I noticed that you recently removed all content from {{<includeonly>safesubst:</includeonly>#if:{{{section|}}}|a section at}} {{<includeonly>safesubst:</includeonly>#if:{{{1|}}}|[[:{{{1}}}]]|a page}}. [[WP:No blank pages|Please do not do this]], as blank pages can confuse readers. * If you discover a duplicate article, please [[Wikipedia:Redirect|redirect]] it to another appropriate page. * If a page has been [[Wikipedia:How to spot vandalism|vandalised]], please [[Wikipedia:How to revert a page to an earlier version|revert]] it to the last good version. * If page content has issues, please improve it by [[Wikipedia:How to edit a page|editing]] the page. * If you believe a page should be deleted, please see the [[WP:BEFORE|deletion process]]. * If you wish to experiment, please use {{safesubst:<noinclude/>sandbox link}}. If this was a mistake, don't worry; the removed content has been restored. If you think I made a mistake or if you have any questions, you can leave a message on [[User talk:<includeonly>{{safesub<noinclude></noinclude>st:REVISIONUSER}}</includeonly><noinclude>Jimbo Wales</noinclude>|my talk page]]. {{{2|Thanks.}}}<!-- Template:uw-blank1 --><noinclude> {{Templatesnotice|series=uw-blank|max=2|escalate=yes|escalate_to=delete|extra_usage= * You can specify that only a section was blanked by adding {{para|section|y}} (or {{para|section|yes}}, etc.). }} </noinclude> 4e7r5iwkesl8b1eaf953kqynfpkclrx ಟೆಂಪ್ಲೇಟ್:Uw-blank2 10 28029 366084 2020-09-12T23:12:19Z en>Sdkb 0 replacing sandbox code with link to template (see [[Wikipedia_talk:Template_index/User_talk_namespace#Sandbox_link|this discussion]]) 366084 wikitext text/x-wiki {{{icon|[[File:Information orange.svg|25px|alt=Information icon]]}}} Please do not replace Wikipedia {{<includeonly>safesubst:</includeonly>#if:{{{section|}}}|page sections|pages}} with blank content{{<includeonly>safesubst:</includeonly>#if:{{{1|}}}|, as you did at [[:{{{1}}}]]}}. Your content removal does not appear to be constructive and has been [[Help:Reverting|reverted]]. If you think that the page should be deleted, please read [[Wikipedia:How to delete a page]]. If you only meant to make a test edit, please use {{safesubst:<noinclude/>sandbox link}} for that. {{{2|Thank you.}}}<!-- Template:uw-blank2 --><noinclude> {{Templatesnotice|series=uw-blank|max=2|escalate=yes|escalate_to=delete|extra_usage= * You can specify that only a section was blanked by adding {{para|section|y}} (or {{para|section|yes}}, etc.). }} </noinclude> 2pfyyflg3mck01ixo8ugijedw0lnda4 366085 366084 2026-08-20T13:00:27Z ChiK 1136 ೧ revisions imported from [[:en:Template:Uw-blank2]] 366084 wikitext text/x-wiki {{{icon|[[File:Information orange.svg|25px|alt=Information icon]]}}} Please do not replace Wikipedia {{<includeonly>safesubst:</includeonly>#if:{{{section|}}}|page sections|pages}} with blank content{{<includeonly>safesubst:</includeonly>#if:{{{1|}}}|, as you did at [[:{{{1}}}]]}}. Your content removal does not appear to be constructive and has been [[Help:Reverting|reverted]]. If you think that the page should be deleted, please read [[Wikipedia:How to delete a page]]. If you only meant to make a test edit, please use {{safesubst:<noinclude/>sandbox link}} for that. {{{2|Thank you.}}}<!-- Template:uw-blank2 --><noinclude> {{Templatesnotice|series=uw-blank|max=2|escalate=yes|escalate_to=delete|extra_usage= * You can specify that only a section was blanked by adding {{para|section|y}} (or {{para|section|yes}}, etc.). }} </noinclude> 2pfyyflg3mck01ixo8ugijedw0lnda4 ಟೆಂಪ್ಲೇಟ್:Uw-blank3 10 28030 366086 2011-02-02T05:48:42Z en>Rich Farmbrough 0 Minor fixes using [[Project:AWB|AWB]] 366086 wikitext text/x-wiki #REDIRECT [[Template:Uw-delete3]] {{R to warning template}} 3wy76bs333l13t33ua7bnbvt591avpq 366087 366086 2026-08-20T13:00:41Z ChiK 1136 ೧ revisions imported from [[:en:Template:Uw-blank3]] 366086 wikitext text/x-wiki #REDIRECT [[Template:Uw-delete3]] {{R to warning template}} 3wy76bs333l13t33ua7bnbvt591avpq ಫತೇಪುರ್ ಸಿಕ್ರಿ 0 28031 366090 2026-08-20T18:03:37Z Kavitha G. Kana 1040 ಹೊಸ ಪುಟ: '''ಫತೇಪುರ ಸಿಕ್ರಿ''' (  ) ಭಾರತ ದೇಶೊದ ಉತ್ತರ ಪ್ರದೇಶದ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಒಂಜಿ ಊರು . 35.7 ಕಿ.ಮೀ,(೨೨.೨mi) ಆಗ್ರಾ ಜಿಲ್ಲಾ ಮುಖ್ಯ ಕಚೇರಿರ್ದ್ ದೂರೊಡು ಉಂಡು,<ref>{{cite web |title=Fatehpur Sikri |url=https://www.tajmahal.gov.in/fatehpur-sikri.aspx |website=www.tajmahal.gov.in |access-date=8 March 2022 |ar... 366090 wikitext text/x-wiki '''ಫತೇಪುರ ಸಿಕ್ರಿ''' (  ) ಭಾರತ ದೇಶೊದ ಉತ್ತರ ಪ್ರದೇಶದ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಒಂಜಿ ಊರು . 35.7 ಕಿ.ಮೀ,(೨೨.೨mi) ಆಗ್ರಾ ಜಿಲ್ಲಾ ಮುಖ್ಯ ಕಚೇರಿರ್ದ್ ದೂರೊಡು ಉಂಡು,<ref>{{cite web |title=Fatehpur Sikri |url=https://www.tajmahal.gov.in/fatehpur-sikri.aspx |website=www.tajmahal.gov.in |access-date=8 March 2022 |archive-date=8 March 2022 |archive-url=https://web.archive.org/web/20220308114239/https://www.tajmahal.gov.in/fatehpur-sikri.aspx |url-status=live }}</ref>ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ 1571ಡ್ ಚಕ್ರವರ್ತಿ ಅಕ್ಬರ್ ಮುಘಲೆರೆನ ಸಾಮ್ರಾಜ್ಯದ ರಾಜಧಾನಿಯಾದ್ ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್, 1571 ಡ್ದ್ 1585 ಮುಟ್ಟ ರಾಜಧಾನಿಯಾದೇ ದೀಯೆರ್. ಅಕ್ಬರ್ ಪಂಜಾಬ್‌ಡ್ ನಡತಿನ ಒಂಜಿ ಪ್ರಚಾರೊದ ಕಾರಣವಾದ್ ಅಯಿನ್ ಬುಡ್ದು ಬುಡಿಯೆರ್, 1610ಡ್ ಪೂರಾ ಅವೆತ ಗಮನೊನ್ ಬುಡ್ದ್ ಬುಡಿಯೆರ್<ref name="Routlegde">{{cite book |url=https://books.google.com/books?id=9A-EAgAAQBAJ&pg=PA82 |title=Dictionary of Islamic Architecture |author=Andrew Petersen |publisher=Routlegde |page=82 |isbn=9781134613656 |date=11 March 2002}}</ref> ಈ ಪೇಂಟೆದ ಪುದರ್ ದುಂಬು ಈ ಜಾಗೆನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಸಿಕ್ರಿ ಪನ್ಪಿನ ಊರುಡ್ದ್ ಬತ್ತ್ಂಡ್. ೧೯೯೯ಡ್ದ್ ೨೦೦೦ ಮುಟ್ಟ ಆರ್ಕಿಯೋಲೊಜಿಕಲ್ ಸರ್ವೇ ಆಫ್ ಇಂಡಿಯಾ (A.S.I.) ಕಾನಿಸ್‌ಮಾರಿ ಮಲ್ತಿನೆಟ್ಟ್ ಅಕ್ಬರ್ ರಾಜಧಾನಿನ್ ಕಟ್ಟೆರೆ ದುಂಬು ಮುಲ್ಪ ವಸತಿ, ದೇವಸ್ಥಾನೊಲು ಬೊಕ್ಕ ವಾಣಿಜ್ಯ ಕೇಂದ್ರೊಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನವು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಂಗಾಸ್ ಪನ್ಪಿನಕ್ಲು ದುಂಬು ನೆಲೆ ಮಲ್ತೆರ್.<ref>{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=A. |last4=Kabui |first4=K. A. |title=Excavation at Birchhabili Tila, Sikri |journal=Puratattva: Journal of the Indian Archaeological Society |issue=31 |year=2001 |pages=55–56 |language=en}}</ref> ಕಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಆಪಿನ ಮುಟ್ಟ ಉಂದು ಸಿಕಾರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ನಿಯಂತ್ರನೊಡು ಇತ್ತ್ಂಡ್.<ref>{{cite book |last1=Patil |first1=Devendrakumar Rajaram |title=The Antiquarian Remains in Bihar |date=1963 |publisher=Kashi Prasad Jayaswal Research Institute |page=75 |url=https://books.google.com/books?id=9xNXAAAAMAAJ&q=sakarwar+rajputs+fatehpur+sikri |language=en}}</ref><ref name=":1" /> ಶೇಖ್ ಸಲೀಂ ಚಿಸ್ಟಿನ ಕಾನ್ಕಾ ದುಂಬು ಈ ಜಾಗೆಡ್ ಇತ್ತ್ಂಡ್. ಅಕ್ಬರ್ ನ ಮಗೆ, ಜಹಾಂಗೀರ್, 1569ಡ್ ಸಿಕ್ರಿ ಗ್ರಾಮೊಡು ಅಕ್ಬರೆನ ಮೋಕೆದ ಬುಡೆದಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಪುಟ್ಟುವೆರ್,<ref>{{cite book|last1=Hindu Shah|first1=Muhammad Qasim|title=Gulshan-I-Ibrahimi|page=223}}</ref> ಆ ವರ್ಷೊಡು, ಪುಟ್ಟುನೆನ್ ದುಂಬಾದ್ ಸೂಚನೆ ಮಲ್ತಿನ ಶೇಖ್‌ನ ನೆಂಪುಗಾದ್ ಒಂಜಿ ಧಾರ್ಮಿಕ ಗುಂಪುನು ಸುರು ಮಲ್ತೆರ್. ಜಹಾಂಗೀರ್‌ನ ರಡ್ಡನೆ ಪುಟ್ಟು ಪರ್ಬೊಗ್ ಅಕ್ಬರ್, ಒಂಜಿ ಗೋಡೆದ ನಗರ ಬೊಕ್ಕ ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಅರಮನೆದ ನಿರ್ಮಾಣೊನು ಸುರು ಮಲ್ತೆರ್. ೧೫೭೩ಡ್ ಅಕ್ಬರ್‌ನ ಗುಜರಾತ್‌ದ ವಿಜಯಶಾಲಿ ಪ್ರಚಾರೊದ ಬೊಕ್ಕ ಈ ನಗರೊನು ಫತೇಪುರ್ ಸಿಕ್ರಿ "ವಿಜಯದ ನಗರ" ಪನ್ಪಿನ ಪುದರ್‌‌ಡ್ ಗುರ್ತ ಮಲ್ತೆರ್. ೧೮೦೩ಡ್ ಆಗ್ರಾನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಬೊಕ್ಕ ಈಸ್ಟ್ ಇಂಡಿಯಾ ಕಂಪೆನಿ ಮುಲ್ಪ ಒಂಜಿ ಆಡಳಿತ ಕೇಂದ್ರ ಸ್ಥಾಪನೆ ಮಲ್ತ್ ದ್ ೧೮೫೦ ಮುಟ್ಟ ಇಂಚೆನೆ ಉಂತುಂಡು. ೧೮೧೫ಡ್, ಮಾರ್ಕ್ವೆಸ್ ಆಫ್ ಹ್ಯಾಸ್ಟಿಂಗ್ಸ್ ಸಿಕ್ರಿಡ್ ಸ್ಮಾರಕೊಲೆನ್ ದುರಸ್ತಿ ಮಲ್ಪೆರೆ ಆದೇಶ ಮಲ್ತೆರ್. ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯದ ರಾಜಧಾನಿ ಆದ್ ಐತಿಹಾಸಿಕ ಮಹತ್ವ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಗ್[೧] ಕಾರಣವಾಯಿನ, ಫತೇಪುರ್ ಸಿಕ್ರಿಗ್ 1986ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾನೊದ ಸ್ಥಾನಮಾನೊನು ತಿಕ್ಕುಂಡ್.<ref>{{cite web |title=Fatehpur Sikri |url=https://whc.unesco.org/en/list/255/ |website=UNESCO World Heritage Centre |access-date=15 February 2022 |language=en |archive-date=15 February 2022 |archive-url=https://web.archive.org/web/20220215162920/https://whc.unesco.org/en/list/255/ |url-status=live }}</ref><ref>{{Cite web|url = https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri/ |title = 30 Years as World Heritage Site: Fatehpur Sikri|date = 22 March 2016|access-date = 13 January 2022|archive-date = 13 January 2022|archive-url = https://web.archive.org/web/20220113152418/https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri |url-status=live}}</ref> ==ಇತಿಹಾಸೊ== ಪುರಾತತ್ತ್ವ ಶಾಸ್ತ್ರೊದ ಸಾಕ್ಷಿದ ಪ್ರಕಾರ ಪೇಂಟೆಡ್ ಗ್ರೇ ವೇರ್ ಕಾಲೊಡ್ದ್ ಬೊಕ್ಕ ಈ ಜಾಗೆಡ್ ಜನಜೀವನ ಇತ್ತ್ಂಡ್ ಪನ್ಪುನ ತೋಜಿದ್ ಬೈದ್ಂಡ್. ಇತಿಹಾಸಕಾರೆ ಸೈಯದ್ ಅಲಿ ನದೀಮ್ ರೆಜಾವಿನ ಪ್ರಕಾರ, ಈ ಪ್ರದೇಶ ಸುಂಗಾ ಆಡಳಿತೊಡು ಬೊಕ್ಕ ಸಿಕರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ಆಡಳಿತೊಡು ಬುಲೆಂಡ್, ಅಕುಲು 7ನೆಡ್ದ್ 16ನೆ ಶತಮಾನೊಗು ಖಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಮುಟ್ಟ ಈ ಪ್ರದೇಶೊನು ನಿಯಂತ್ರಣ ಮಲ್ಪುನಗ ಒಂಜಿ ಕೋಟೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಪ್ರದೇಶೊ ಬೊಕ್ಕ ದೆಹಲಿ ಸುಲ್ತಾನತೆದ ಆಡಳಿತೊಗು ಬತ್ತ್ಂಡ್ಬೊ.ಖಾಲ್ಜಿ ರಾಜವಂಶೊದ ಕಾಲೊಡು ಗಾತ್ರೊಡು ಬುಲೆಯಿನ ಜಾಗೆಡ್ ಮಸ್ತ್ ಮಸೀದಿಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name=":1">{{cite book |last1=Rezavi |first1=Syed Ali Nadeem |title=Sikri before Akbar |date=2013 |publisher=Oxford University Press |isbn=978-0-19-908256-8 |url=https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |access-date=13 January 2020 |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113124948/https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |url-status=live }}</ref><ref>{{cite news |last1=Safvi |first1=Rana |title=The secrets about Fatehpur Sikri |url=https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |access-date=13 January 2020 |work=The Hindu |date=10 December 2017 |language=en-IN |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113125020/https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |url-status=live }}</ref> ==ಮುಘಲೆರ್ದ್ ದುಂಬುದ ಕಾಲ== ದುಂಬು ಇತ್ತಿನ ಕೆದುತ ಆಗ್ನೇಯ ತೀರೊಡು ಉಪ್ಪುನ ಬಿರ್ಛಾಬಿಲಿ ತಿಲಾಡ್ ನಡತಿನ ಸರ್ವೆಡ್ ಕ್ರಿ.ಶ.೬ನೆ ಶತಮಾನೊಗು ದುಂಬು ಒಂಜಿ ವಸಾಹತು ಇತ್ತ್ಂಡು ಪನ್ಪುನ ಪಳಯುಲಿಕೆಲ್ ತಿಕ್ಕುಂಡು.<!-- Sharma et al. 2001, pp. 56-7 --> ೬ನೆಡ್ದ್ ೯ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನ ಪಿರಾಕ್ ದ ಪದರೊಡು ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು, ಕಟ್ಟಡೊಲೆನ ಅಡಿಪಾಯೊಲು, ಒಂಜಿ ಆಕಾರೊದ ಕಲ್ಲುದ ಸ್ತಂಭ, ಬೊಕ್ಕ ಕೆಂಪು ಸ್ಯಾಂಡ್‌ಸ್ಟೋನ್‌ಡ್<!-- Sharma et al. 2001, p. 57 --> ಮಲ್ತಿನ ನರಮಾನಿಲೇ ಮಲ್ತಿನ ಗುಂಡಿ ಸೇರ್ದ್ ಬೇತೆ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅವಶೇಸೋಲು ಉಂಡು. ಜೈನ ದೇವತೆ ಅಂಬಿಕಾನ ತುಂಡಾತಿನ ಶಿಲ್ಪ ಶೈಲಿಶಾಸ್ತ್ರೀಯವಾದ್ 6ನೆ ಅತ್ತಂದೆ 7ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನವು, ಕಲ್ಲ್ ದ ಸ್ತಂಭ ಬೊಕ್ಕ ಬ್ರಾಹ್ಮೀ ಲಿಪಿಟ್ ರಡ್ಡ್ ಅಕ್ಷರೊದ ಶಾಸನೊ, ಈ ಕಾಲೊಡು ಜೈನ ದೇಗುಲೊದ ಇತ್ತಿನೆನ್ ತೋಜಾವುಂಡು.<!-- Sharma et al. 2001, p. 58 --> ನನಾತ್ ಅಗೆತಿನೆಟ್ಟ್ ಬತ್ತಿನ ಬೇತೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಗೊಬ್ಬುನೆತ್ತ ತುಂಡುಲು, ಬರವುದ ವಸ್ತುಲು, ಅಚ್ಚುಲು, ಕುಪ್ಪಿದ ಬೊಕ್ಕ ಕರ್ಬೊದ ಕಾಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಈಟಿ ಪೂರಾ ತಿಕ್ಕುದಿತ್ತ್ಂಡು.<!-- Sharma et al. 2001, p. 58 --><ref name="Sharma et al. 2001">{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=V. |last4=Kabui |first4=K. I. |title=Excavation at Birchhabili Tila, Sikri |journal=Purātattva |date=2001 |volume=31 |pages=55–72 |url=https://www.academia.edu/43373675/Excavation_at_Birchhabili_Tila_Sikri |access-date=7 March 2026}}</ref> ==ಭಾಸೆ== ೨೦೧೧ ರ ಜನಗಣತಿ ಪ್ರಕಾರ ೯೮.೮೧% ಜನಕುಲು ಹಿಂದಿ ಪಾತೆರುನಕುಲು ಬೊಕ್ಕ ೧.೦೪% ಜನಕುಲು ಬ್ರಜ್ ಭಾಷಾ ಪಾತೆರುನಕುಲು ಪಂಡ್‌ದ್ ತೆರಿದುಂಡು.<ref>{{cite web |title=C-16 Population By Mother Tongue - Town level |url=https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |website=censusindia.gov.in |access-date=2 April 2021 |archive-date=28 July 2021 |archive-url=https://web.archive.org/web/20210728041052/https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |url-status=live }}</ref> ==ಸರಕಾರ ಬೊಕ್ಕ ರಾಜಕೀಯ== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಪದಿನೈನ್ ಬ್ಲಾಕ್‌ದ ಮುಖ್ಯ ಕಚೇರಿಲೆಡ್ ಒಂಜಿ. ಉಂದೆತ ಅಡಿಟ್ ೫೨ ಗ್ರಾಮ ಪಂಚಾಯತ್‌ ಉಂಡು. ಫತೇಪುರ್ ಸಿಕ್ರಿ, ಲೋಕಸಭೆದ ಒಂಜಿ ಕ್ಷೇತ್ರ, ಭಾರತೀಯ ಸಂಸತ್‌ ದ ಲೋಕ ಸಬೆಟ್ (Lower house), ಬೊಕ್ಕ ಐನ್ ವಿಧಾನಸಭೆ ( ಶಾಸಕಾಂಗ ಸಭೆ ) ವಿಭಾಗೊಲೆನ್ ಸೇರ್‌ದುಂಡು: ಆಗ್ರಾ ಗ್ರಾಮೀಣ | ಫತೇಪುರ ಸಿಕ್ರಿ ಖೇರಾಗಢ್ ಫತೇಹಾಬಾದ್ ಬಾಹ್ ==ವಾಸ್ತುಶಿಲ್ಪಲು== [[File:General Plan of Fatehpur Sikri City India 1917.jpg|thumb|250px|೧೯೧೭ನೆತ್ತ ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಸಾಮಾನ್ಯ ಯೋಜನೆ.]] ಫತೇಪುರ್ ಸಿಕ್ರಿ ಒಂಜಿ ಕಲ್ಲುದ ಗುಡ್ಡೆಡ್ ಉಂಡು, 3 kilometres (1.9 mi) ಉದ್ದೊಡು ಬೊಕ್ಕ 1 km (0.62 mi) ಅಗಲ, ಬೊಕ್ಕ ಅರಮನೆದ ನಗರೊನು 6 km (3.7 mi) ಡ್ದ್ ಸುತ್ತಲಾ ಉಂಡು ಮೂಜಿ ಬದಿಟ್ ಗೋಡೆ ನಾಲ್ನೆ ಕೆದುತ ಗಡಿಟ್ ಉಂಡು. ಸಾಮಾನ್ಯವಾದ್ ಈ 40 ಮೀ ಎತ್ತರದ ಗುಡ್ಡೆದ ಸುತ್ತ ಏರ್ಪಾಟ್ ಆತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದು ಸರಿಸುಮಾರು ಒಂಜಿ ರೊಂಬಸ್ ಆಕಾರೊಡು ಬೂರುಂಡು. ನೆಲತ ರಚನೆಲೆನ ಸಾಮಾನ್ಯ ವಿನ್ಯಾಸ, ವಿಶೇಷವಾದ್ "ತೋಟೊಲೆನ ನಿರಂತರ ಬೊಕ್ಕ ಕಾಂಪ್ಯಾಕ್ಟ್ ನಮೂನೆ ಬೊಕ್ಕ ಸೇವೆಲು ಬೊಕ್ಕ ಸೌಲಭ್ಯೊಲು" ನಗರೊನು ಗುರ್ತಮಲ್ಪೆರೆ ಆಪುಂಡು. ನಗರ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆನ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಮುಖ್ಯವಾದ್ ಅಯಿತ ಪ್ರಸಿದ್ಧ ನಿವಾಸಿಲೆಗ್ ವಿರಾಮ ಬೊಕ್ಕ ಐಷಾರಾಮಿ ಸೌಲಭ್ಯೊಲೆನ್ ಕೊರ್ಪುನಂಚ ಕಟ್ಟ್‌ದೆರ್ ಪನ್ಪಿನ ತೀರ್ಮಾನೊಗು ಕಾರಣವಾತ್ಂಡ್.. ಫತೇಹ್ಪುರ್ ಸಿಕ್ರಿದ ರಾಜವಂಶದ ವಾಸ್ತುಶಿಲ್ಪೊನು ಟಿಮುರಿಡ್ ರೂಪೊಲು ಬೊಕ್ಕ ಶೈಲಿಲೆನ್ ಆಧಾರವಾದ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Islam: Art and Architecture|author=Markus Hattstein, Peter Delius|publisher=Könemann|year=2000|pages=466}}</ref> The city was built massively and preferably with red sandstone.<ref>{{cite book|url=https://books.google.com/books?id=P2R3a3KLl2IC&pg=PA1|title=Mughal Architecture|author=Moritz Herrmann|publisher=GRIN Verlag|year=2011|page=3|isbn=9783640930036}}</ref> ನಗರೊನು ಮಸ್ತ್ ಮಟ್ಟ್‌ಡ್ ಬೊಕ್ಕ ಕೆಂಪು ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Mughal Architecture: An Outline of Its History and Development (1526-1858)|author=Ebba Koch|publisher=Prestel|year=1991|page=60|author-link=Ebba Koch}}</ref> ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಅರಮನೆಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಶಬ್ದಕೋಶೊಡು ಬೊಕ್ಕ ಅಲಂಕಾರೊಡುಲಾ ಗುಜರಾತಿದ ಪ್ರಭಾವೊಲು ತೋಜುಂಡು. <ref>{{cite book|url=https://books.google.com/books?id=3ctLNvx68hIC&pg=PA50|title=Architecture of Mughal India, Part 1, Volume 4|author=Catherine Ella Blanshard Asher|publisher=Cambridge University Press|year=1992|page=50|isbn=9780521267281}}</ref> ನಗರದ ವಾಸ್ತುಶಿಲ್ಪ ಆ ಕಾಲೊಡು ಭಾರತೊಡು ಜನಪ್ರಿಯವಾದಿತ್ತಿನ ದೇಶೀಯ ವಾಸ್ತುಶಿಲ್ಪದ ಹಿಂದೂ ಬೊಕ್ಕ ಮುಸ್ಲಿಂ ರೂಪೊಲೆನ್ ಪ್ರತಿಬಿಂಬಿಸವುಂಡು. ಈ ಮೂಲ ಜಾಗೊಲೆನ ಗಮನಾರ್ಹ ಸಂರಕ್ಷಣೆ ಆಧುನಿಕ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆಗ್ ಮೊಘಲ್ ನ್ಯಾಯಾಲಯದ ಜೀವನದ ದೃಶ್ಯೊಲೆನ್ ಪಿರ ಕಟ್ಟೆರೆ ಸುಲಭ ಮಲ್ಪುಂಡು, ಬೊಕ್ಕ ನಗರದ ರಾಜವಂಶದ ಬೊಕ್ಕ ಶ್ರೀಮಂತ ನಿವಾಸಿಲೆನ ಶ್ರೇಣಿ ವ್ಯವಸ್ಥೆನ್ ಎಡ್ಡೆ ರೀತಿಡ್ ಅರ್ಥ ಮಲ್ಪುಂಡು. 5 miles (8.0 km) ದೂರೊಡು ಉಪ್ಪುನ ಗೇಟ್ ಲೆಡ್ದ್ ಉಂದೆನ್ ಪೊಗ್ಗೊಲಿ ಉದ್ದದ ಕೋಟೆ ಗೋಡೆ, ಅವು, ದೆಹಲಿ ದ್ವಾರ, ಲಾಲ್ ದ್ವಾರ, ಆಗ್ರಾ ದ್ವಾರ ಬೊಕ್ಕ ಬಿರ್ಬಲ್ ದ್ವಾರ, ಚಂದನ್ಪಾಲ್ ದ್ವಾರ, ಗ್ವಾಲಿಯರ್ ದ್ವಾರ, ತೆಹ್ರಾ ದ್ವಾರ, ಚೋರ್ ದ್ವಾರ, ಬೊಕ್ಕ ಅಜ್ಮೇರಿ ದ್ವಾರ. ಈ ಅರಮನೆಡ್ ರಾಣಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಬೇಸಿಗೆದ ಅರಮನೆ ಬೊಕ್ಕ ಚಳಿಗಾಲದ ಅರಮನೆ ಉಂಡು, ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಜೋಧಾ ಬಾಯಿ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. [[File:Jama Masjid-Sikri-Fatehpur Sikri-India0008.JPG|thumb|250px|[[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Fatehpur Sikiri Salim Chishti Tomb 2010.jpg|thumb|250px|[[ಸಲೀಂ ಚಿಸ್ಟಿನ ಗೋರಿ]] in [[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ|ಜಮಾ ಮಸೀದಿ]] ಕೋರ್ಟ್‌‌ಯಾರ್ಡ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Panch Mahal-Fatehpur-Fatehpur Sikri India0014.JPG|thumb|250px|[[ಪಂಚ್ ಮಹಲ್, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Hiran Minar FatehpurSikri.jpg|thumb|ಹಿರನ್ ಮಿನಾರ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Fatehpur Sikri Fort 166.jpg|thumb|Rear view of the grand gateway of [[ಜೋದಾ ಬಾಯಿ ಮಹಲ್]], ಫತೇಪುರ ಸಿಕ್ರಿಡ್ ಮಲ್ಲ ಜನಮಾನಿ ಇತ್ತಿನ ಮಹಲ್]] [[File:Monuments in Fatehpur Sikri.jpg|thumb|ಸ್ಮಾರಕೊಲು, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Rear View of Jodha Bai Palace, Fatehpur Sikri.jpg|thumb|ಜೋದ್ ಬಾಯಿ ಮಹಲ್‌ದ ಕೈತಲ್‌ದ ಚಿತ್ರನೊ, ಫತೇಪುರ ಸಿಕ್ರಿ]] ==ಸಾಗಾವುನಿ (ಬಂಡಿ)== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಸುಮಾರ್ 37 kilometres (23 mi) ಆಗ್ರಾ‌ಡ್ದ್.<ref>{{Cite encyclopedia |title=Fatehpur Sikri - Architecture, Foundation, & History |url=https://www.britannica.com/place/Fatehpur-Sikri |encyclopedia=Britannica |date=28 December 2024 |access-date=1 January 2025}}</ref> ಕೈತಲ್‌ದ ವಿಮಾನ ನಿಲ್ದಾಣ ಆಗ್ರಾ ವಿಮಾನ ನಿಲ್ದಾಣ ( ಖೇರಿಯಾ ವಿಮಾನ ನಿಲ್ದಾಣ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್), 40 kilometres (25 mi) ಫತೇಪುರ್ ಸಿಕ್ರಿ‌ಡ್ದ್. ಕೈತಲ್‌ದ ರೈಲ್ವೆ ನಿಲ್ದಾಣ ಫತೇಪುರ್ ಸಿಕ್ರಿ ರೈಲ್ವೆ ನಿಲ್ದಾಣ, ಸುಮಾರ್ 1 kilometre (0.62 mi) ನಗರ ಕೇಂದ್ರೊಡ್ದ್. ಉಂದು ಆಗ್ರಾ ಬೊಕ್ಕ ನೆರೆಕರೆತ ಕೇಂದ್ರೊಲೆಗ್ ರಸ್ತೆಡ್ ಸಂಪರ್ಕೊಡು ಉಂಡು, ಅಲ್ಪ ಪ್ರವಾಸಿ ಬಸ್ಸುಲು ಬೊಕ್ಕ ಟ್ಯಾಕ್ಸಿಲೆನ ಒಟ್ಟಿಗೆಯುಪಿಎಸ್ಆರ್ಟಿಸಿ ನಿಯಮಿತ ಬಸ್ ಸೇವೆಲೆನ್ ಕೊರ್ಪುಂಡು. ==ಜನಪ್ರಿಯ ಸಂಸ್ಕೃತಿಡ್== ಸಲ್ಮಾನ್ ರುಶ್ದಿ ನ ಕಾದಂಬರಿಡ್ ದಿ ಎನ್ಚಾಂಟ್ರೆಸ್ ಆಫ್ ಫ್ಲೋರೆನ್ಸ್ 16ನೆ ಶತಮಾನೊದ ಫತೇಪುರ್ ಸಿಕ್ರಿಡ್ ನಡತ್ಂಡ್. <ref>{{cite news |last1=Choudhury |first1=Chandrahas |title=An ode to adolescent fantasy |url=https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |access-date=27 March 2022 |work=Mint |date=18 April 2008 |language=en |archive-date=27 March 2022 |archive-url=https://web.archive.org/web/20220327194048/https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |url-status=live }}</ref> ==ಸಿನಿಮಾಲೆಡ್== ಶಾರುಖ್ ಖಾನ್ ಬೊಕ್ಕ ಮಹಿಮಾ ಚೌಧರಿ ನಟನೆ ಮಲ್ತಿನ ಹಿಂದಿ ಚಿತ್ರ ಪರ್ದೇಸ್‌ಡ್ದ್ ೧೯೯೭ಡ್ ಹಿಟ್ ಆಯಿನ "ದೋ ದಿಲ್ ಮಿಲ್ ರಾಹೇ ಹೈನ್" ಪನ್ಪಿನ ಪದನ್ ನಗರೊಡು ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್.<ref>{{Cite web |url=https://www.youtube.com/watch?v=gmJW33derRY |title=Do Dil Mil Rahe Hain from movie Pardes 1997|date=4 March 2022 |publisher=YouTube}}</ref> ==ಉಲ್ಲೇಕ== {{Reflist}} 79xtcfsfrlf1ihubdj4t36pm2hp10eq 366101 366090 2026-08-21T02:35:24Z ChiK 1136 /* */ 366101 wikitext text/x-wiki '''ಫತೇಪುರ ಸಿಕ್ರಿ''' ಭಾರತ ದೇಶೊದ ಉತ್ತರ ಪ್ರದೇಶದ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಒಂಜಿ ಊರು . 35.7 ಕಿ.ಮೀ,(೨೨.೨mi) ಆಗ್ರಾ ಜಿಲ್ಲಾ ಮುಖ್ಯ ಕಚೇರಿರ್ದ್ ದೂರೊಡು ಉಂಡು,<ref>{{cite web |title=Fatehpur Sikri |url=https://www.tajmahal.gov.in/fatehpur-sikri.aspx |website=www.tajmahal.gov.in |access-date=8 March 2022 |archive-date=8 March 2022 |archive-url=https://web.archive.org/web/20220308114239/https://www.tajmahal.gov.in/fatehpur-sikri.aspx |url-status=live }}</ref>ಚಕ್ರವರ್ತಿ ಅಕ್ಬರ್ 1571ಡ್ ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯೊದ ರಾಜಧಾನಿಯಾದ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಉಂಡ್ ಮಲ್ತೆರ್; ಉಂದು 1571 ಡ್ದ್ 1585 ಮುಟ್ಟ ಈ ಸ್ಥಾನಮಾನೊನು ಪಡೆಯೊಂದಿತ್ತ್ಂಡ್. ಪಂಜಾಬ್‌ಡ್ ನಡತಿನ ಒಂಜಿ ಅಭಿಯಾನೊಗು ಕಾರಣವಾದ್ ಅಕ್ಬರ್ ಉಂದೆನ್ ಬುಡ್ದು ಪೋಯೆರ್, ಬೊಕ್ಕ 1610 ಡ್ದ್ ಉಂದು ಪೂರ್ತಿಯಾದ್ ಕೈಬುಡಿಯೆರ್.<ref name="Routlegde">{{cite book |url=https://books.google.com/books?id=9A-EAgAAQBAJ&pg=PA82 |title=Dictionary of Islamic Architecture |author=Andrew Petersen |publisher=Routlegde |page=82 |isbn=9781134613656 |date=11 March 2002}}</ref> ಈ ಪೇಂಟೆದ ಪುದರ್ ದುಂಬು ಈ ಜಾಗೆನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಸಿಕ್ರಿ ಪನ್ಪಿನ ಊರುಡ್ದ್ ಬತ್ತ್ಂಡ್. ೧೯೯೯ಡ್ದ್ ೨೦೦೦ ಮುಟ್ಟ ಆರ್ಕಿಯೋಲೊಜಿಕಲ್ ಸರ್ವೇ ಆಫ್ ಇಂಡಿಯಾ (A.S.I.) ಕಾನಿಸ್‌ಮಾರಿ ಮಲ್ತಿನೆಟ್ಟ್ ಅಕ್ಬರ್ ರಾಜಧಾನಿನ್ ಕಟ್ಟೆರೆ ದುಂಬು ಮುಲ್ಪ ವಸತಿ, ದೇವಸ್ಥಾನೊಲು ಬೊಕ್ಕ ವಾಣಿಜ್ಯ ಕೇಂದ್ರೊಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನವು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಂಗಾಸ್ ಪನ್ಪಿನಕ್ಲು ದುಂಬು ನೆಲೆ ಮಲ್ತೆರ್.<ref>{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=A. |last4=Kabui |first4=K. A. |title=Excavation at Birchhabili Tila, Sikri |journal=Puratattva: Journal of the Indian Archaeological Society |issue=31 |year=2001 |pages=55–56 |language=en}}</ref> ಕಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಆಪಿನ ಮುಟ್ಟ ಉಂದು ಸಿಕಾರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ನಿಯಂತ್ರನೊಡು ಇತ್ತ್ಂಡ್.<ref>{{cite book |last1=Patil |first1=Devendrakumar Rajaram |title=The Antiquarian Remains in Bihar |date=1963 |publisher=Kashi Prasad Jayaswal Research Institute |page=75 |url=https://books.google.com/books?id=9xNXAAAAMAAJ&q=sakarwar+rajputs+fatehpur+sikri |language=en}}</ref><ref name=":1" /> ಶೇಖ್ ಸಲೀಂ ಚಿಸ್ಟಿನ ಕಾನ್ಕಾ ದುಂಬು ಈ ಜಾಗೆಡ್ ಇತ್ತ್ಂಡ್. ಅಕ್ಬರ್ ನ ಮಗೆ, ಜಹಾಂಗೀರ್, 1569ಡ್ ಸಿಕ್ರಿ ಗ್ರಾಮೊಡು ಅಕ್ಬರೆನ ಮೋಕೆದ ಬುಡೆದಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಪುಟ್ಟುವೆರ್,<ref>{{cite book|last1=Hindu Shah|first1=Muhammad Qasim|title=Gulshan-I-Ibrahimi|page=223}}</ref> ಆ ವರ್ಷೊಡು, ಪುಟ್ಟುನೆನ್ ದುಂಬಾದ್ ಸೂಚನೆ ಮಲ್ತಿನ ಶೇಖ್‌ನ ನೆಂಪುಗಾದ್ ಒಂಜಿ ಧಾರ್ಮಿಕ ಗುಂಪುನು ಸುರು ಮಲ್ತೆರ್. ಜಹಾಂಗೀರ್‌ನ ರಡ್ಡನೆ ಪುಟ್ಟು ಪರ್ಬೊಗ್ ಅಕ್ಬರ್, ಒಂಜಿ ಗೋಡೆದ ನಗರ ಬೊಕ್ಕ ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಅರಮನೆದ ನಿರ್ಮಾಣೊನು ಸುರು ಮಲ್ತೆರ್. ೧೫೭೩ಡ್ ಅಕ್ಬರ್‌ನ ಗುಜರಾತ್‌ದ ವಿಜಯಶಾಲಿ ಪ್ರಚಾರೊದ ಬೊಕ್ಕ ಈ ನಗರೊನು ಫತೇಪುರ್ ಸಿಕ್ರಿ "ವಿಜಯದ ನಗರ" ಪನ್ಪಿನ ಪುದರ್‌‌ಡ್ ಗುರ್ತ ಮಲ್ತೆರ್. ೧೮೦೩ಡ್ ಆಗ್ರಾನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಬೊಕ್ಕ ಈಸ್ಟ್ ಇಂಡಿಯಾ ಕಂಪೆನಿ ಮುಲ್ಪ ಒಂಜಿ ಆಡಳಿತ ಕೇಂದ್ರ ಸ್ಥಾಪನೆ ಮಲ್ತ್ ದ್ ೧೮೫೦ ಮುಟ್ಟ ಇಂಚೆನೆ ಉಂತುಂಡು. ೧೮೧೫ಡ್, ಮಾರ್ಕ್ವೆಸ್ ಆಫ್ ಹ್ಯಾಸ್ಟಿಂಗ್ಸ್ ಸಿಕ್ರಿಡ್ ಸ್ಮಾರಕೊಲೆನ್ ದುರಸ್ತಿ ಮಲ್ಪೆರೆ ಆದೇಶ ಮಲ್ತೆರ್. ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯದ ರಾಜಧಾನಿ ಆದ್ ಐತಿಹಾಸಿಕ ಮಹತ್ವ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಗ್[೧] ಕಾರಣವಾಯಿನ, ಫತೇಪುರ್ ಸಿಕ್ರಿಗ್ 1986ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾನೊದ ಸ್ಥಾನಮಾನೊನು ತಿಕ್ಕುಂಡ್.<ref>{{cite web |title=Fatehpur Sikri |url=https://whc.unesco.org/en/list/255/ |website=UNESCO World Heritage Centre |access-date=15 February 2022 |language=en |archive-date=15 February 2022 |archive-url=https://web.archive.org/web/20220215162920/https://whc.unesco.org/en/list/255/ |url-status=live }}</ref><ref>{{Cite web|url = https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri/ |title = 30 Years as World Heritage Site: Fatehpur Sikri|date = 22 March 2016|access-date = 13 January 2022|archive-date = 13 January 2022|archive-url = https://web.archive.org/web/20220113152418/https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri |url-status=live}}</ref> ==ಇತಿಹಾಸೊ== ಪುರಾತತ್ತ್ವ ಶಾಸ್ತ್ರೊದ ಸಾಕ್ಷಿದ ಪ್ರಕಾರ ಪೇಂಟೆಡ್ ಗ್ರೇ ವೇರ್ ಕಾಲೊಡ್ದ್ ಬೊಕ್ಕ ಈ ಜಾಗೆಡ್ ಜನಜೀವನ ಇತ್ತ್ಂಡ್ ಪನ್ಪುನ ತೋಜಿದ್ ಬೈದ್ಂಡ್. ಇತಿಹಾಸಕಾರೆ ಸೈಯದ್ ಅಲಿ ನದೀಮ್ ರೆಜಾವಿನ ಪ್ರಕಾರ, ಈ ಪ್ರದೇಶ ಸುಂಗಾ ಆಡಳಿತೊಡು ಬೊಕ್ಕ ಸಿಕರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ಆಡಳಿತೊಡು ಬುಲೆಂಡ್, ಅಕುಲು 7ನೆಡ್ದ್ 16ನೆ ಶತಮಾನೊಗು ಖಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಮುಟ್ಟ ಈ ಪ್ರದೇಶೊನು ನಿಯಂತ್ರಣ ಮಲ್ಪುನಗ ಒಂಜಿ ಕೋಟೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಪ್ರದೇಶೊ ಬೊಕ್ಕ ದೆಹಲಿ ಸುಲ್ತಾನತೆದ ಆಡಳಿತೊಗು ಬತ್ತ್ಂಡ್ಬೊ.ಖಾಲ್ಜಿ ರಾಜವಂಶೊದ ಕಾಲೊಡು ಗಾತ್ರೊಡು ಬುಲೆಯಿನ ಜಾಗೆಡ್ ಮಸ್ತ್ ಮಸೀದಿಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name=":1">{{cite book |last1=Rezavi |first1=Syed Ali Nadeem |title=Sikri before Akbar |date=2013 |publisher=Oxford University Press |isbn=978-0-19-908256-8 |url=https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |access-date=13 January 2020 |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113124948/https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |url-status=live }}</ref><ref>{{cite news |last1=Safvi |first1=Rana |title=The secrets about Fatehpur Sikri |url=https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |access-date=13 January 2020 |work=The Hindu |date=10 December 2017 |language=en-IN |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113125020/https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |url-status=live }}</ref> ==ಮುಘಲೆರ್ದ್ ದುಂಬುದ ಕಾಲ== ದುಂಬು ಇತ್ತಿನ ಕೆದುತ ಆಗ್ನೇಯ ತೀರೊಡು ಉಪ್ಪುನ ಬಿರ್ಛಾಬಿಲಿ ತಿಲಾಡ್ ನಡತಿನ ಸರ್ವೆಡ್ ಕ್ರಿ.ಶ.೬ನೆ ಶತಮಾನೊಗು ದುಂಬು ಒಂಜಿ ವಸಾಹತು ಇತ್ತ್ಂಡು ಪನ್ಪುನ ಪಳಯುಲಿಕೆಲ್ ತಿಕ್ಕುಂಡು.<!-- Sharma et al. 2001, pp. 56-7 --> ೬ನೆಡ್ದ್ ೯ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನ ಪಿರಾಕ್ ದ ಪದರೊಡು ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು, ಕಟ್ಟಡೊಲೆನ ಅಡಿಪಾಯೊಲು, ಒಂಜಿ ಆಕಾರೊದ ಕಲ್ಲುದ ಸ್ತಂಭ, ಬೊಕ್ಕ ಕೆಂಪು ಸ್ಯಾಂಡ್‌ಸ್ಟೋನ್‌ಡ್<!-- Sharma et al. 2001, p. 57 --> ಮಲ್ತಿನ ನರಮಾನಿಲೇ ಮಲ್ತಿನ ಗುಂಡಿ ಸೇರ್ದ್ ಬೇತೆ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅವಶೇಸೋಲು ಉಂಡು. ಜೈನ ದೇವತೆ ಅಂಬಿಕಾನ ತುಂಡಾತಿನ ಶಿಲ್ಪ ಶೈಲಿಶಾಸ್ತ್ರೀಯವಾದ್ 6ನೆ ಅತ್ತಂದೆ 7ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನವು, ಕಲ್ಲ್ ದ ಸ್ತಂಭ ಬೊಕ್ಕ ಬ್ರಾಹ್ಮೀ ಲಿಪಿಟ್ ರಡ್ಡ್ ಅಕ್ಷರೊದ ಶಾಸನೊ, ಈ ಕಾಲೊಡು ಜೈನ ದೇಗುಲೊದ ಇತ್ತಿನೆನ್ ತೋಜಾವುಂಡು.<!-- Sharma et al. 2001, p. 58 --> ನನಾತ್ ಅಗೆತಿನೆಟ್ಟ್ ಬತ್ತಿನ ಬೇತೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಗೊಬ್ಬುನೆತ್ತ ತುಂಡುಲು, ಬರವುದ ವಸ್ತುಲು, ಅಚ್ಚುಲು, ಕುಪ್ಪಿದ ಬೊಕ್ಕ ಕರ್ಬೊದ ಕಾಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಈಟಿ ಪೂರಾ ತಿಕ್ಕುದಿತ್ತ್ಂಡು.<!-- Sharma et al. 2001, p. 58 --><ref name="Sharma et al. 2001">{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=V. |last4=Kabui |first4=K. I. |title=Excavation at Birchhabili Tila, Sikri |journal=Purātattva |date=2001 |volume=31 |pages=55–72 |url=https://www.academia.edu/43373675/Excavation_at_Birchhabili_Tila_Sikri |access-date=7 March 2026}}</ref> ==ಭಾಸೆ== ೨೦೧೧ ರ ಜನಗಣತಿ ಪ್ರಕಾರ ೯೮.೮೧% ಜನಕುಲು ಹಿಂದಿ ಪಾತೆರುನಕುಲು ಬೊಕ್ಕ ೧.೦೪% ಜನಕುಲು ಬ್ರಜ್ ಭಾಷಾ ಪಾತೆರುನಕುಲು ಪಂಡ್‌ದ್ ತೆರಿದುಂಡು.<ref>{{cite web |title=C-16 Population By Mother Tongue - Town level |url=https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |website=censusindia.gov.in |access-date=2 April 2021 |archive-date=28 July 2021 |archive-url=https://web.archive.org/web/20210728041052/https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |url-status=live }}</ref> ==ಸರಕಾರ ಬೊಕ್ಕ ರಾಜಕೀಯ== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಪದಿನೈನ್ ಬ್ಲಾಕ್‌ದ ಮುಖ್ಯ ಕಚೇರಿಲೆಡ್ ಒಂಜಿ. ಉಂದೆತ ಅಡಿಟ್ ೫೨ ಗ್ರಾಮ ಪಂಚಾಯತ್‌ ಉಂಡು. ಫತೇಪುರ್ ಸಿಕ್ರಿ, ಲೋಕಸಭೆದ ಒಂಜಿ ಕ್ಷೇತ್ರ, ಭಾರತೀಯ ಸಂಸತ್‌ ದ ಲೋಕ ಸಬೆಟ್ (Lower house), ಬೊಕ್ಕ ಐನ್ ವಿಧಾನಸಭೆ ( ಶಾಸಕಾಂಗ ಸಭೆ ) ವಿಭಾಗೊಲೆನ್ ಸೇರ್‌ದುಂಡು: ಆಗ್ರಾ ಗ್ರಾಮೀಣ | ಫತೇಪುರ ಸಿಕ್ರಿ ಖೇರಾಗಢ್ ಫತೇಹಾಬಾದ್ ಬಾಹ್ ==ವಾಸ್ತುಶಿಲ್ಪಲು== [[File:General Plan of Fatehpur Sikri City India 1917.jpg|thumb|250px|೧೯೧೭ನೆತ್ತ ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಸಾಮಾನ್ಯ ಯೋಜನೆ.]] ಫತೇಪುರ್ ಸಿಕ್ರಿ ಒಂಜಿ ಕಲ್ಲುದ ಗುಡ್ಡೆಡ್ ಉಂಡು, 3 kilometres (1.9 mi) ಉದ್ದೊಡು ಬೊಕ್ಕ 1 km (0.62 mi) ಅಗಲ, ಬೊಕ್ಕ ಅರಮನೆದ ನಗರೊನು 6 km (3.7 mi) ಡ್ದ್ ಸುತ್ತಲಾ ಉಂಡು ಮೂಜಿ ಬದಿಟ್ ಗೋಡೆ ನಾಲ್ನೆ ಕೆದುತ ಗಡಿಟ್ ಉಂಡು. ಸಾಮಾನ್ಯವಾದ್ ಈ 40 ಮೀ ಎತ್ತರದ ಗುಡ್ಡೆದ ಸುತ್ತ ಏರ್ಪಾಟ್ ಆತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದು ಸರಿಸುಮಾರು ಒಂಜಿ ರೊಂಬಸ್ ಆಕಾರೊಡು ಬೂರುಂಡು. ನೆಲತ ರಚನೆಲೆನ ಸಾಮಾನ್ಯ ವಿನ್ಯಾಸ, ವಿಶೇಷವಾದ್ "ತೋಟೊಲೆನ ನಿರಂತರ ಬೊಕ್ಕ ಕಾಂಪ್ಯಾಕ್ಟ್ ನಮೂನೆ ಬೊಕ್ಕ ಸೇವೆಲು ಬೊಕ್ಕ ಸೌಲಭ್ಯೊಲು" ನಗರೊನು ಗುರ್ತಮಲ್ಪೆರೆ ಆಪುಂಡು. ನಗರ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆನ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಮುಖ್ಯವಾದ್ ಅಯಿತ ಪ್ರಸಿದ್ಧ ನಿವಾಸಿಲೆಗ್ ವಿರಾಮ ಬೊಕ್ಕ ಐಷಾರಾಮಿ ಸೌಲಭ್ಯೊಲೆನ್ ಕೊರ್ಪುನಂಚ ಕಟ್ಟ್‌ದೆರ್ ಪನ್ಪಿನ ತೀರ್ಮಾನೊಗು ಕಾರಣವಾತ್ಂಡ್.. ಫತೇಹ್ಪುರ್ ಸಿಕ್ರಿದ ರಾಜವಂಶದ ವಾಸ್ತುಶಿಲ್ಪೊನು ಟಿಮುರಿಡ್ ರೂಪೊಲು ಬೊಕ್ಕ ಶೈಲಿಲೆನ್ ಆಧಾರವಾದ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Islam: Art and Architecture|author=Markus Hattstein, Peter Delius|publisher=Könemann|year=2000|pages=466}}</ref> The city was built massively and preferably with red sandstone.<ref>{{cite book|url=https://books.google.com/books?id=P2R3a3KLl2IC&pg=PA1|title=Mughal Architecture|author=Moritz Herrmann|publisher=GRIN Verlag|year=2011|page=3|isbn=9783640930036}}</ref> ನಗರೊನು ಮಸ್ತ್ ಮಟ್ಟ್‌ಡ್ ಬೊಕ್ಕ ಕೆಂಪು ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Mughal Architecture: An Outline of Its History and Development (1526-1858)|author=Ebba Koch|publisher=Prestel|year=1991|page=60|author-link=Ebba Koch}}</ref> ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಅರಮನೆಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಶಬ್ದಕೋಶೊಡು ಬೊಕ್ಕ ಅಲಂಕಾರೊಡುಲಾ ಗುಜರಾತಿದ ಪ್ರಭಾವೊಲು ತೋಜುಂಡು. <ref>{{cite book|url=https://books.google.com/books?id=3ctLNvx68hIC&pg=PA50|title=Architecture of Mughal India, Part 1, Volume 4|author=Catherine Ella Blanshard Asher|publisher=Cambridge University Press|year=1992|page=50|isbn=9780521267281}}</ref> ನಗರದ ವಾಸ್ತುಶಿಲ್ಪ ಆ ಕಾಲೊಡು ಭಾರತೊಡು ಜನಪ್ರಿಯವಾದಿತ್ತಿನ ದೇಶೀಯ ವಾಸ್ತುಶಿಲ್ಪದ ಹಿಂದೂ ಬೊಕ್ಕ ಮುಸ್ಲಿಂ ರೂಪೊಲೆನ್ ಪ್ರತಿಬಿಂಬಿಸವುಂಡು. ಈ ಮೂಲ ಜಾಗೊಲೆನ ಗಮನಾರ್ಹ ಸಂರಕ್ಷಣೆ ಆಧುನಿಕ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆಗ್ ಮೊಘಲ್ ನ್ಯಾಯಾಲಯದ ಜೀವನದ ದೃಶ್ಯೊಲೆನ್ ಪಿರ ಕಟ್ಟೆರೆ ಸುಲಭ ಮಲ್ಪುಂಡು, ಬೊಕ್ಕ ನಗರದ ರಾಜವಂಶದ ಬೊಕ್ಕ ಶ್ರೀಮಂತ ನಿವಾಸಿಲೆನ ಶ್ರೇಣಿ ವ್ಯವಸ್ಥೆನ್ ಎಡ್ಡೆ ರೀತಿಡ್ ಅರ್ಥ ಮಲ್ಪುಂಡು. 5 miles (8.0 km) ದೂರೊಡು ಉಪ್ಪುನ ಗೇಟ್ ಲೆಡ್ದ್ ಉಂದೆನ್ ಪೊಗ್ಗೊಲಿ ಉದ್ದದ ಕೋಟೆ ಗೋಡೆ, ಅವು, ದೆಹಲಿ ದ್ವಾರ, ಲಾಲ್ ದ್ವಾರ, ಆಗ್ರಾ ದ್ವಾರ ಬೊಕ್ಕ ಬಿರ್ಬಲ್ ದ್ವಾರ, ಚಂದನ್ಪಾಲ್ ದ್ವಾರ, ಗ್ವಾಲಿಯರ್ ದ್ವಾರ, ತೆಹ್ರಾ ದ್ವಾರ, ಚೋರ್ ದ್ವಾರ, ಬೊಕ್ಕ ಅಜ್ಮೇರಿ ದ್ವಾರ. ಈ ಅರಮನೆಡ್ ರಾಣಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಬೇಸಿಗೆದ ಅರಮನೆ ಬೊಕ್ಕ ಚಳಿಗಾಲದ ಅರಮನೆ ಉಂಡು, ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಜೋಧಾ ಬಾಯಿ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. [[File:Jama Masjid-Sikri-Fatehpur Sikri-India0008.JPG|thumb|250px|[[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Fatehpur Sikiri Salim Chishti Tomb 2010.jpg|thumb|250px|[[ಸಲೀಂ ಚಿಸ್ಟಿನ ಗೋರಿ]] in [[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ|ಜಮಾ ಮಸೀದಿ]] ಕೋರ್ಟ್‌‌ಯಾರ್ಡ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Panch Mahal-Fatehpur-Fatehpur Sikri India0014.JPG|thumb|250px|[[ಪಂಚ್ ಮಹಲ್, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Hiran Minar FatehpurSikri.jpg|thumb|ಹಿರನ್ ಮಿನಾರ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Fatehpur Sikri Fort 166.jpg|thumb|Rear view of the grand gateway of [[ಜೋದಾ ಬಾಯಿ ಮಹಲ್]], ಫತೇಪುರ ಸಿಕ್ರಿಡ್ ಮಲ್ಲ ಜನಮಾನಿ ಇತ್ತಿನ ಮಹಲ್]] [[File:Monuments in Fatehpur Sikri.jpg|thumb|ಸ್ಮಾರಕೊಲು, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Rear View of Jodha Bai Palace, Fatehpur Sikri.jpg|thumb|ಜೋದ್ ಬಾಯಿ ಮಹಲ್‌ದ ಕೈತಲ್‌ದ ಚಿತ್ರನೊ, ಫತೇಪುರ ಸಿಕ್ರಿ]] ==ಸಾಗಾವುನಿ (ಬಂಡಿ)== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಸುಮಾರ್ 37 kilometres (23 mi) ಆಗ್ರಾ‌ಡ್ದ್.<ref>{{Cite encyclopedia |title=Fatehpur Sikri - Architecture, Foundation, & History |url=https://www.britannica.com/place/Fatehpur-Sikri |encyclopedia=Britannica |date=28 December 2024 |access-date=1 January 2025}}</ref> ಕೈತಲ್‌ದ ವಿಮಾನ ನಿಲ್ದಾಣ ಆಗ್ರಾ ವಿಮಾನ ನಿಲ್ದಾಣ ( ಖೇರಿಯಾ ವಿಮಾನ ನಿಲ್ದಾಣ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್), 40 kilometres (25 mi) ಫತೇಪುರ್ ಸಿಕ್ರಿ‌ಡ್ದ್. ಕೈತಲ್‌ದ ರೈಲ್ವೆ ನಿಲ್ದಾಣ ಫತೇಪುರ್ ಸಿಕ್ರಿ ರೈಲ್ವೆ ನಿಲ್ದಾಣ, ಸುಮಾರ್ 1 kilometre (0.62 mi) ನಗರ ಕೇಂದ್ರೊಡ್ದ್. ಉಂದು ಆಗ್ರಾ ಬೊಕ್ಕ ನೆರೆಕರೆತ ಕೇಂದ್ರೊಲೆಗ್ ರಸ್ತೆಡ್ ಸಂಪರ್ಕೊಡು ಉಂಡು, ಅಲ್ಪ ಪ್ರವಾಸಿ ಬಸ್ಸುಲು ಬೊಕ್ಕ ಟ್ಯಾಕ್ಸಿಲೆನ ಒಟ್ಟಿಗೆಯುಪಿಎಸ್ಆರ್ಟಿಸಿ ನಿಯಮಿತ ಬಸ್ ಸೇವೆಲೆನ್ ಕೊರ್ಪುಂಡು. ==ಜನಪ್ರಿಯ ಸಂಸ್ಕೃತಿಡ್== ಸಲ್ಮಾನ್ ರುಶ್ದಿ ನ ಕಾದಂಬರಿಡ್ ದಿ ಎನ್ಚಾಂಟ್ರೆಸ್ ಆಫ್ ಫ್ಲೋರೆನ್ಸ್ 16ನೆ ಶತಮಾನೊದ ಫತೇಪುರ್ ಸಿಕ್ರಿಡ್ ನಡತ್ಂಡ್. <ref>{{cite news |last1=Choudhury |first1=Chandrahas |title=An ode to adolescent fantasy |url=https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |access-date=27 March 2022 |work=Mint |date=18 April 2008 |language=en |archive-date=27 March 2022 |archive-url=https://web.archive.org/web/20220327194048/https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |url-status=live }}</ref> ==ಸಿನಿಮಾಲೆಡ್== ಶಾರುಖ್ ಖಾನ್ ಬೊಕ್ಕ ಮಹಿಮಾ ಚೌಧರಿ ನಟನೆ ಮಲ್ತಿನ ಹಿಂದಿ ಚಿತ್ರ ಪರ್ದೇಸ್‌ಡ್ದ್ ೧೯೯೭ಡ್ ಹಿಟ್ ಆಯಿನ "ದೋ ದಿಲ್ ಮಿಲ್ ರಾಹೇ ಹೈನ್" ಪನ್ಪಿನ ಪದನ್ ನಗರೊಡು ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್.<ref>{{Cite web |url=https://www.youtube.com/watch?v=gmJW33derRY |title=Do Dil Mil Rahe Hain from movie Pardes 1997|date=4 March 2022 |publisher=YouTube}}</ref> ==ಉಲ್ಲೇಕ== {{Reflist}} 2h6w0ff8jx7rb5frk48a0qiuza9dee9 366102 366101 2026-08-21T02:41:33Z ChiK 1136 Added {{[[Template:Copy edit|Copy edit]]}} tag 366102 wikitext text/x-wiki {{Copy edit|for=ಅರ್ತ ಆನಂದಿ ವಾಕ್ಯೊಲು|date=ಆಗೋಸ್ಟು ೨೦೨೬}} '''ಫತೇಪುರ ಸಿಕ್ರಿ''' ಭಾರತ ದೇಶೊದ ಉತ್ತರ ಪ್ರದೇಶದ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಒಂಜಿ ಊರು . 35.7 ಕಿ.ಮೀ,(೨೨.೨mi) ಆಗ್ರಾ ಜಿಲ್ಲಾ ಮುಖ್ಯ ಕಚೇರಿರ್ದ್ ದೂರೊಡು ಉಂಡು,<ref>{{cite web |title=Fatehpur Sikri |url=https://www.tajmahal.gov.in/fatehpur-sikri.aspx |website=www.tajmahal.gov.in |access-date=8 March 2022 |archive-date=8 March 2022 |archive-url=https://web.archive.org/web/20220308114239/https://www.tajmahal.gov.in/fatehpur-sikri.aspx |url-status=live }}</ref>ಚಕ್ರವರ್ತಿ ಅಕ್ಬರ್ 1571ಡ್ ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯೊದ ರಾಜಧಾನಿಯಾದ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಉಂಡ್ ಮಲ್ತೆರ್; ಉಂದು 1571 ಡ್ದ್ 1585 ಮುಟ್ಟ ಈ ಸ್ಥಾನಮಾನೊನು ಪಡೆಯೊಂದಿತ್ತ್ಂಡ್. ಪಂಜಾಬ್‌ಡ್ ನಡತಿನ ಒಂಜಿ ಅಭಿಯಾನೊಗು ಕಾರಣವಾದ್ ಅಕ್ಬರ್ ಉಂದೆನ್ ಬುಡ್ದು ಪೋಯೆರ್, ಬೊಕ್ಕ 1610 ಡ್ದ್ ಉಂದು ಪೂರ್ತಿಯಾದ್ ಕೈಬುಡಿಯೆರ್.<ref name="Routlegde">{{cite book |url=https://books.google.com/books?id=9A-EAgAAQBAJ&pg=PA82 |title=Dictionary of Islamic Architecture |author=Andrew Petersen |publisher=Routlegde |page=82 |isbn=9781134613656 |date=11 March 2002}}</ref> ಈ ಪೇಂಟೆದ ಪುದರ್ ದುಂಬು ಈ ಜಾಗೆನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಸಿಕ್ರಿ ಪನ್ಪಿನ ಊರುಡ್ದ್ ಬತ್ತ್ಂಡ್. ೧೯೯೯ಡ್ದ್ ೨೦೦೦ ಮುಟ್ಟ ಆರ್ಕಿಯೋಲೊಜಿಕಲ್ ಸರ್ವೇ ಆಫ್ ಇಂಡಿಯಾ (A.S.I.) ಕಾನಿಸ್‌ಮಾರಿ ಮಲ್ತಿನೆಟ್ಟ್ ಅಕ್ಬರ್ ರಾಜಧಾನಿನ್ ಕಟ್ಟೆರೆ ದುಂಬು ಮುಲ್ಪ ವಸತಿ, ದೇವಸ್ಥಾನೊಲು ಬೊಕ್ಕ ವಾಣಿಜ್ಯ ಕೇಂದ್ರೊಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನವು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಂಗಾಸ್ ಪನ್ಪಿನಕ್ಲು ದುಂಬು ನೆಲೆ ಮಲ್ತೆರ್.<ref>{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=A. |last4=Kabui |first4=K. A. |title=Excavation at Birchhabili Tila, Sikri |journal=Puratattva: Journal of the Indian Archaeological Society |issue=31 |year=2001 |pages=55–56 |language=en}}</ref> ಕಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಆಪಿನ ಮುಟ್ಟ ಉಂದು ಸಿಕಾರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ನಿಯಂತ್ರನೊಡು ಇತ್ತ್ಂಡ್.<ref>{{cite book |last1=Patil |first1=Devendrakumar Rajaram |title=The Antiquarian Remains in Bihar |date=1963 |publisher=Kashi Prasad Jayaswal Research Institute |page=75 |url=https://books.google.com/books?id=9xNXAAAAMAAJ&q=sakarwar+rajputs+fatehpur+sikri |language=en}}</ref><ref name=":1" /> ಶೇಖ್ ಸಲೀಂ ಚಿಸ್ಟಿನ ಕಾನ್ಕಾ ದುಂಬು ಈ ಜಾಗೆಡ್ ಇತ್ತ್ಂಡ್. ಅಕ್ಬರ್ ನ ಮಗೆ, ಜಹಾಂಗೀರ್, 1569ಡ್ ಸಿಕ್ರಿ ಗ್ರಾಮೊಡು ಅಕ್ಬರೆನ ಮೋಕೆದ ಬುಡೆದಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಪುಟ್ಟುವೆರ್,<ref>{{cite book|last1=Hindu Shah|first1=Muhammad Qasim|title=Gulshan-I-Ibrahimi|page=223}}</ref> ಆ ವರ್ಷೊಡು, ಪುಟ್ಟುನೆನ್ ದುಂಬಾದ್ ಸೂಚನೆ ಮಲ್ತಿನ ಶೇಖ್‌ನ ನೆಂಪುಗಾದ್ ಒಂಜಿ ಧಾರ್ಮಿಕ ಗುಂಪುನು ಸುರು ಮಲ್ತೆರ್. ಜಹಾಂಗೀರ್‌ನ ರಡ್ಡನೆ ಪುಟ್ಟು ಪರ್ಬೊಗ್ ಅಕ್ಬರ್, ಒಂಜಿ ಗೋಡೆದ ನಗರ ಬೊಕ್ಕ ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಅರಮನೆದ ನಿರ್ಮಾಣೊನು ಸುರು ಮಲ್ತೆರ್. ೧೫೭೩ಡ್ ಅಕ್ಬರ್‌ನ ಗುಜರಾತ್‌ದ ವಿಜಯಶಾಲಿ ಪ್ರಚಾರೊದ ಬೊಕ್ಕ ಈ ನಗರೊನು ಫತೇಪುರ್ ಸಿಕ್ರಿ "ವಿಜಯದ ನಗರ" ಪನ್ಪಿನ ಪುದರ್‌‌ಡ್ ಗುರ್ತ ಮಲ್ತೆರ್. ೧೮೦೩ಡ್ ಆಗ್ರಾನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಬೊಕ್ಕ ಈಸ್ಟ್ ಇಂಡಿಯಾ ಕಂಪೆನಿ ಮುಲ್ಪ ಒಂಜಿ ಆಡಳಿತ ಕೇಂದ್ರ ಸ್ಥಾಪನೆ ಮಲ್ತ್ ದ್ ೧೮೫೦ ಮುಟ್ಟ ಇಂಚೆನೆ ಉಂತುಂಡು. ೧೮೧೫ಡ್, ಮಾರ್ಕ್ವೆಸ್ ಆಫ್ ಹ್ಯಾಸ್ಟಿಂಗ್ಸ್ ಸಿಕ್ರಿಡ್ ಸ್ಮಾರಕೊಲೆನ್ ದುರಸ್ತಿ ಮಲ್ಪೆರೆ ಆದೇಶ ಮಲ್ತೆರ್. ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯದ ರಾಜಧಾನಿ ಆದ್ ಐತಿಹಾಸಿಕ ಮಹತ್ವ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಗ್[೧] ಕಾರಣವಾಯಿನ, ಫತೇಪುರ್ ಸಿಕ್ರಿಗ್ 1986ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾನೊದ ಸ್ಥಾನಮಾನೊನು ತಿಕ್ಕುಂಡ್.<ref>{{cite web |title=Fatehpur Sikri |url=https://whc.unesco.org/en/list/255/ |website=UNESCO World Heritage Centre |access-date=15 February 2022 |language=en |archive-date=15 February 2022 |archive-url=https://web.archive.org/web/20220215162920/https://whc.unesco.org/en/list/255/ |url-status=live }}</ref><ref>{{Cite web|url = https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri/ |title = 30 Years as World Heritage Site: Fatehpur Sikri|date = 22 March 2016|access-date = 13 January 2022|archive-date = 13 January 2022|archive-url = https://web.archive.org/web/20220113152418/https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri |url-status=live}}</ref> ==ಇತಿಹಾಸೊ== ಪುರಾತತ್ತ್ವ ಶಾಸ್ತ್ರೊದ ಸಾಕ್ಷಿದ ಪ್ರಕಾರ ಪೇಂಟೆಡ್ ಗ್ರೇ ವೇರ್ ಕಾಲೊಡ್ದ್ ಬೊಕ್ಕ ಈ ಜಾಗೆಡ್ ಜನಜೀವನ ಇತ್ತ್ಂಡ್ ಪನ್ಪುನ ತೋಜಿದ್ ಬೈದ್ಂಡ್. ಇತಿಹಾಸಕಾರೆ ಸೈಯದ್ ಅಲಿ ನದೀಮ್ ರೆಜಾವಿನ ಪ್ರಕಾರ, ಈ ಪ್ರದೇಶ ಸುಂಗಾ ಆಡಳಿತೊಡು ಬೊಕ್ಕ ಸಿಕರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ಆಡಳಿತೊಡು ಬುಲೆಂಡ್, ಅಕುಲು 7ನೆಡ್ದ್ 16ನೆ ಶತಮಾನೊಗು ಖಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಮುಟ್ಟ ಈ ಪ್ರದೇಶೊನು ನಿಯಂತ್ರಣ ಮಲ್ಪುನಗ ಒಂಜಿ ಕೋಟೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಪ್ರದೇಶೊ ಬೊಕ್ಕ ದೆಹಲಿ ಸುಲ್ತಾನತೆದ ಆಡಳಿತೊಗು ಬತ್ತ್ಂಡ್ಬೊ.ಖಾಲ್ಜಿ ರಾಜವಂಶೊದ ಕಾಲೊಡು ಗಾತ್ರೊಡು ಬುಲೆಯಿನ ಜಾಗೆಡ್ ಮಸ್ತ್ ಮಸೀದಿಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name=":1">{{cite book |last1=Rezavi |first1=Syed Ali Nadeem |title=Sikri before Akbar |date=2013 |publisher=Oxford University Press |isbn=978-0-19-908256-8 |url=https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |access-date=13 January 2020 |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113124948/https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |url-status=live }}</ref><ref>{{cite news |last1=Safvi |first1=Rana |title=The secrets about Fatehpur Sikri |url=https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |access-date=13 January 2020 |work=The Hindu |date=10 December 2017 |language=en-IN |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113125020/https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |url-status=live }}</ref> ==ಮುಘಲೆರ್ದ್ ದುಂಬುದ ಕಾಲ== ದುಂಬು ಇತ್ತಿನ ಕೆದುತ ಆಗ್ನೇಯ ತೀರೊಡು ಉಪ್ಪುನ ಬಿರ್ಛಾಬಿಲಿ ತಿಲಾಡ್ ನಡತಿನ ಸರ್ವೆಡ್ ಕ್ರಿ.ಶ.೬ನೆ ಶತಮಾನೊಗು ದುಂಬು ಒಂಜಿ ವಸಾಹತು ಇತ್ತ್ಂಡು ಪನ್ಪುನ ಪಳಯುಲಿಕೆಲ್ ತಿಕ್ಕುಂಡು.<!-- Sharma et al. 2001, pp. 56-7 --> ೬ನೆಡ್ದ್ ೯ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನ ಪಿರಾಕ್ ದ ಪದರೊಡು ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು, ಕಟ್ಟಡೊಲೆನ ಅಡಿಪಾಯೊಲು, ಒಂಜಿ ಆಕಾರೊದ ಕಲ್ಲುದ ಸ್ತಂಭ, ಬೊಕ್ಕ ಕೆಂಪು ಸ್ಯಾಂಡ್‌ಸ್ಟೋನ್‌ಡ್<!-- Sharma et al. 2001, p. 57 --> ಮಲ್ತಿನ ನರಮಾನಿಲೇ ಮಲ್ತಿನ ಗುಂಡಿ ಸೇರ್ದ್ ಬೇತೆ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅವಶೇಸೋಲು ಉಂಡು. ಜೈನ ದೇವತೆ ಅಂಬಿಕಾನ ತುಂಡಾತಿನ ಶಿಲ್ಪ ಶೈಲಿಶಾಸ್ತ್ರೀಯವಾದ್ 6ನೆ ಅತ್ತಂದೆ 7ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನವು, ಕಲ್ಲ್ ದ ಸ್ತಂಭ ಬೊಕ್ಕ ಬ್ರಾಹ್ಮೀ ಲಿಪಿಟ್ ರಡ್ಡ್ ಅಕ್ಷರೊದ ಶಾಸನೊ, ಈ ಕಾಲೊಡು ಜೈನ ದೇಗುಲೊದ ಇತ್ತಿನೆನ್ ತೋಜಾವುಂಡು.<!-- Sharma et al. 2001, p. 58 --> ನನಾತ್ ಅಗೆತಿನೆಟ್ಟ್ ಬತ್ತಿನ ಬೇತೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಗೊಬ್ಬುನೆತ್ತ ತುಂಡುಲು, ಬರವುದ ವಸ್ತುಲು, ಅಚ್ಚುಲು, ಕುಪ್ಪಿದ ಬೊಕ್ಕ ಕರ್ಬೊದ ಕಾಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಈಟಿ ಪೂರಾ ತಿಕ್ಕುದಿತ್ತ್ಂಡು.<!-- Sharma et al. 2001, p. 58 --><ref name="Sharma et al. 2001">{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=V. |last4=Kabui |first4=K. I. |title=Excavation at Birchhabili Tila, Sikri |journal=Purātattva |date=2001 |volume=31 |pages=55–72 |url=https://www.academia.edu/43373675/Excavation_at_Birchhabili_Tila_Sikri |access-date=7 March 2026}}</ref> ==ಭಾಸೆ== ೨೦೧೧ ರ ಜನಗಣತಿ ಪ್ರಕಾರ ೯೮.೮೧% ಜನಕುಲು ಹಿಂದಿ ಪಾತೆರುನಕುಲು ಬೊಕ್ಕ ೧.೦೪% ಜನಕುಲು ಬ್ರಜ್ ಭಾಷಾ ಪಾತೆರುನಕುಲು ಪಂಡ್‌ದ್ ತೆರಿದುಂಡು.<ref>{{cite web |title=C-16 Population By Mother Tongue - Town level |url=https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |website=censusindia.gov.in |access-date=2 April 2021 |archive-date=28 July 2021 |archive-url=https://web.archive.org/web/20210728041052/https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |url-status=live }}</ref> ==ಸರಕಾರ ಬೊಕ್ಕ ರಾಜಕೀಯ== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಪದಿನೈನ್ ಬ್ಲಾಕ್‌ದ ಮುಖ್ಯ ಕಚೇರಿಲೆಡ್ ಒಂಜಿ. ಉಂದೆತ ಅಡಿಟ್ ೫೨ ಗ್ರಾಮ ಪಂಚಾಯತ್‌ ಉಂಡು. ಫತೇಪುರ್ ಸಿಕ್ರಿ, ಲೋಕಸಭೆದ ಒಂಜಿ ಕ್ಷೇತ್ರ, ಭಾರತೀಯ ಸಂಸತ್‌ ದ ಲೋಕ ಸಬೆಟ್ (Lower house), ಬೊಕ್ಕ ಐನ್ ವಿಧಾನಸಭೆ ( ಶಾಸಕಾಂಗ ಸಭೆ ) ವಿಭಾಗೊಲೆನ್ ಸೇರ್‌ದುಂಡು: ಆಗ್ರಾ ಗ್ರಾಮೀಣ | ಫತೇಪುರ ಸಿಕ್ರಿ ಖೇರಾಗಢ್ ಫತೇಹಾಬಾದ್ ಬಾಹ್ ==ವಾಸ್ತುಶಿಲ್ಪಲು== [[File:General Plan of Fatehpur Sikri City India 1917.jpg|thumb|250px|೧೯೧೭ನೆತ್ತ ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಸಾಮಾನ್ಯ ಯೋಜನೆ.]] ಫತೇಪುರ್ ಸಿಕ್ರಿ ಒಂಜಿ ಕಲ್ಲುದ ಗುಡ್ಡೆಡ್ ಉಂಡು, 3 kilometres (1.9 mi) ಉದ್ದೊಡು ಬೊಕ್ಕ 1 km (0.62 mi) ಅಗಲ, ಬೊಕ್ಕ ಅರಮನೆದ ನಗರೊನು 6 km (3.7 mi) ಡ್ದ್ ಸುತ್ತಲಾ ಉಂಡು ಮೂಜಿ ಬದಿಟ್ ಗೋಡೆ ನಾಲ್ನೆ ಕೆದುತ ಗಡಿಟ್ ಉಂಡು. ಸಾಮಾನ್ಯವಾದ್ ಈ 40 ಮೀ ಎತ್ತರದ ಗುಡ್ಡೆದ ಸುತ್ತ ಏರ್ಪಾಟ್ ಆತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದು ಸರಿಸುಮಾರು ಒಂಜಿ ರೊಂಬಸ್ ಆಕಾರೊಡು ಬೂರುಂಡು. ನೆಲತ ರಚನೆಲೆನ ಸಾಮಾನ್ಯ ವಿನ್ಯಾಸ, ವಿಶೇಷವಾದ್ "ತೋಟೊಲೆನ ನಿರಂತರ ಬೊಕ್ಕ ಕಾಂಪ್ಯಾಕ್ಟ್ ನಮೂನೆ ಬೊಕ್ಕ ಸೇವೆಲು ಬೊಕ್ಕ ಸೌಲಭ್ಯೊಲು" ನಗರೊನು ಗುರ್ತಮಲ್ಪೆರೆ ಆಪುಂಡು. ನಗರ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆನ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಮುಖ್ಯವಾದ್ ಅಯಿತ ಪ್ರಸಿದ್ಧ ನಿವಾಸಿಲೆಗ್ ವಿರಾಮ ಬೊಕ್ಕ ಐಷಾರಾಮಿ ಸೌಲಭ್ಯೊಲೆನ್ ಕೊರ್ಪುನಂಚ ಕಟ್ಟ್‌ದೆರ್ ಪನ್ಪಿನ ತೀರ್ಮಾನೊಗು ಕಾರಣವಾತ್ಂಡ್.. ಫತೇಹ್ಪುರ್ ಸಿಕ್ರಿದ ರಾಜವಂಶದ ವಾಸ್ತುಶಿಲ್ಪೊನು ಟಿಮುರಿಡ್ ರೂಪೊಲು ಬೊಕ್ಕ ಶೈಲಿಲೆನ್ ಆಧಾರವಾದ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Islam: Art and Architecture|author=Markus Hattstein, Peter Delius|publisher=Könemann|year=2000|pages=466}}</ref> The city was built massively and preferably with red sandstone.<ref>{{cite book|url=https://books.google.com/books?id=P2R3a3KLl2IC&pg=PA1|title=Mughal Architecture|author=Moritz Herrmann|publisher=GRIN Verlag|year=2011|page=3|isbn=9783640930036}}</ref> ನಗರೊನು ಮಸ್ತ್ ಮಟ್ಟ್‌ಡ್ ಬೊಕ್ಕ ಕೆಂಪು ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Mughal Architecture: An Outline of Its History and Development (1526-1858)|author=Ebba Koch|publisher=Prestel|year=1991|page=60|author-link=Ebba Koch}}</ref> ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಅರಮನೆಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಶಬ್ದಕೋಶೊಡು ಬೊಕ್ಕ ಅಲಂಕಾರೊಡುಲಾ ಗುಜರಾತಿದ ಪ್ರಭಾವೊಲು ತೋಜುಂಡು. <ref>{{cite book|url=https://books.google.com/books?id=3ctLNvx68hIC&pg=PA50|title=Architecture of Mughal India, Part 1, Volume 4|author=Catherine Ella Blanshard Asher|publisher=Cambridge University Press|year=1992|page=50|isbn=9780521267281}}</ref> ನಗರದ ವಾಸ್ತುಶಿಲ್ಪ ಆ ಕಾಲೊಡು ಭಾರತೊಡು ಜನಪ್ರಿಯವಾದಿತ್ತಿನ ದೇಶೀಯ ವಾಸ್ತುಶಿಲ್ಪದ ಹಿಂದೂ ಬೊಕ್ಕ ಮುಸ್ಲಿಂ ರೂಪೊಲೆನ್ ಪ್ರತಿಬಿಂಬಿಸವುಂಡು. ಈ ಮೂಲ ಜಾಗೊಲೆನ ಗಮನಾರ್ಹ ಸಂರಕ್ಷಣೆ ಆಧುನಿಕ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆಗ್ ಮೊಘಲ್ ನ್ಯಾಯಾಲಯದ ಜೀವನದ ದೃಶ್ಯೊಲೆನ್ ಪಿರ ಕಟ್ಟೆರೆ ಸುಲಭ ಮಲ್ಪುಂಡು, ಬೊಕ್ಕ ನಗರದ ರಾಜವಂಶದ ಬೊಕ್ಕ ಶ್ರೀಮಂತ ನಿವಾಸಿಲೆನ ಶ್ರೇಣಿ ವ್ಯವಸ್ಥೆನ್ ಎಡ್ಡೆ ರೀತಿಡ್ ಅರ್ಥ ಮಲ್ಪುಂಡು. 5 miles (8.0 km) ದೂರೊಡು ಉಪ್ಪುನ ಗೇಟ್ ಲೆಡ್ದ್ ಉಂದೆನ್ ಪೊಗ್ಗೊಲಿ ಉದ್ದದ ಕೋಟೆ ಗೋಡೆ, ಅವು, ದೆಹಲಿ ದ್ವಾರ, ಲಾಲ್ ದ್ವಾರ, ಆಗ್ರಾ ದ್ವಾರ ಬೊಕ್ಕ ಬಿರ್ಬಲ್ ದ್ವಾರ, ಚಂದನ್ಪಾಲ್ ದ್ವಾರ, ಗ್ವಾಲಿಯರ್ ದ್ವಾರ, ತೆಹ್ರಾ ದ್ವಾರ, ಚೋರ್ ದ್ವಾರ, ಬೊಕ್ಕ ಅಜ್ಮೇರಿ ದ್ವಾರ. ಈ ಅರಮನೆಡ್ ರಾಣಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಬೇಸಿಗೆದ ಅರಮನೆ ಬೊಕ್ಕ ಚಳಿಗಾಲದ ಅರಮನೆ ಉಂಡು, ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಜೋಧಾ ಬಾಯಿ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. [[File:Jama Masjid-Sikri-Fatehpur Sikri-India0008.JPG|thumb|250px|[[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Fatehpur Sikiri Salim Chishti Tomb 2010.jpg|thumb|250px|[[ಸಲೀಂ ಚಿಸ್ಟಿನ ಗೋರಿ]] in [[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ|ಜಮಾ ಮಸೀದಿ]] ಕೋರ್ಟ್‌‌ಯಾರ್ಡ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Panch Mahal-Fatehpur-Fatehpur Sikri India0014.JPG|thumb|250px|[[ಪಂಚ್ ಮಹಲ್, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Hiran Minar FatehpurSikri.jpg|thumb|ಹಿರನ್ ಮಿನಾರ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Fatehpur Sikri Fort 166.jpg|thumb|Rear view of the grand gateway of [[ಜೋದಾ ಬಾಯಿ ಮಹಲ್]], ಫತೇಪುರ ಸಿಕ್ರಿಡ್ ಮಲ್ಲ ಜನಮಾನಿ ಇತ್ತಿನ ಮಹಲ್]] [[File:Monuments in Fatehpur Sikri.jpg|thumb|ಸ್ಮಾರಕೊಲು, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Rear View of Jodha Bai Palace, Fatehpur Sikri.jpg|thumb|ಜೋದ್ ಬಾಯಿ ಮಹಲ್‌ದ ಕೈತಲ್‌ದ ಚಿತ್ರನೊ, ಫತೇಪುರ ಸಿಕ್ರಿ]] ==ಸಾಗಾವುನಿ (ಬಂಡಿ)== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಸುಮಾರ್ 37 kilometres (23 mi) ಆಗ್ರಾ‌ಡ್ದ್.<ref>{{Cite encyclopedia |title=Fatehpur Sikri - Architecture, Foundation, & History |url=https://www.britannica.com/place/Fatehpur-Sikri |encyclopedia=Britannica |date=28 December 2024 |access-date=1 January 2025}}</ref> ಕೈತಲ್‌ದ ವಿಮಾನ ನಿಲ್ದಾಣ ಆಗ್ರಾ ವಿಮಾನ ನಿಲ್ದಾಣ ( ಖೇರಿಯಾ ವಿಮಾನ ನಿಲ್ದಾಣ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್), 40 kilometres (25 mi) ಫತೇಪುರ್ ಸಿಕ್ರಿ‌ಡ್ದ್. ಕೈತಲ್‌ದ ರೈಲ್ವೆ ನಿಲ್ದಾಣ ಫತೇಪುರ್ ಸಿಕ್ರಿ ರೈಲ್ವೆ ನಿಲ್ದಾಣ, ಸುಮಾರ್ 1 kilometre (0.62 mi) ನಗರ ಕೇಂದ್ರೊಡ್ದ್. ಉಂದು ಆಗ್ರಾ ಬೊಕ್ಕ ನೆರೆಕರೆತ ಕೇಂದ್ರೊಲೆಗ್ ರಸ್ತೆಡ್ ಸಂಪರ್ಕೊಡು ಉಂಡು, ಅಲ್ಪ ಪ್ರವಾಸಿ ಬಸ್ಸುಲು ಬೊಕ್ಕ ಟ್ಯಾಕ್ಸಿಲೆನ ಒಟ್ಟಿಗೆಯುಪಿಎಸ್ಆರ್ಟಿಸಿ ನಿಯಮಿತ ಬಸ್ ಸೇವೆಲೆನ್ ಕೊರ್ಪುಂಡು. ==ಜನಪ್ರಿಯ ಸಂಸ್ಕೃತಿಡ್== ಸಲ್ಮಾನ್ ರುಶ್ದಿ ನ ಕಾದಂಬರಿಡ್ ದಿ ಎನ್ಚಾಂಟ್ರೆಸ್ ಆಫ್ ಫ್ಲೋರೆನ್ಸ್ 16ನೆ ಶತಮಾನೊದ ಫತೇಪುರ್ ಸಿಕ್ರಿಡ್ ನಡತ್ಂಡ್. <ref>{{cite news |last1=Choudhury |first1=Chandrahas |title=An ode to adolescent fantasy |url=https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |access-date=27 March 2022 |work=Mint |date=18 April 2008 |language=en |archive-date=27 March 2022 |archive-url=https://web.archive.org/web/20220327194048/https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |url-status=live }}</ref> ==ಸಿನಿಮಾಲೆಡ್== ಶಾರುಖ್ ಖಾನ್ ಬೊಕ್ಕ ಮಹಿಮಾ ಚೌಧರಿ ನಟನೆ ಮಲ್ತಿನ ಹಿಂದಿ ಚಿತ್ರ ಪರ್ದೇಸ್‌ಡ್ದ್ ೧೯೯೭ಡ್ ಹಿಟ್ ಆಯಿನ "ದೋ ದಿಲ್ ಮಿಲ್ ರಾಹೇ ಹೈನ್" ಪನ್ಪಿನ ಪದನ್ ನಗರೊಡು ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್.<ref>{{Cite web |url=https://www.youtube.com/watch?v=gmJW33derRY |title=Do Dil Mil Rahe Hain from movie Pardes 1997|date=4 March 2022 |publisher=YouTube}}</ref> ==ಉಲ್ಲೇಕ== {{Reflist}} 0hh2lajc8gv3yn347y9c0oapv0qv9sj 366104 366102 2026-08-21T02:42:28Z ChiK 1136 366104 wikitext text/x-wiki {{Copy edit|for=ಅರ್ತ ಆವಂದಿ ವಾಕ್ಯೊಲು|date=ಆಗೋಸ್ಟು ೨೦೨೬}} '''ಫತೇಪುರ ಸಿಕ್ರಿ''' ಭಾರತ ದೇಶೊದ ಉತ್ತರ ಪ್ರದೇಶದ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಒಂಜಿ ಊರು . 35.7 ಕಿ.ಮೀ,(೨೨.೨mi) ಆಗ್ರಾ ಜಿಲ್ಲಾ ಮುಖ್ಯ ಕಚೇರಿರ್ದ್ ದೂರೊಡು ಉಂಡು,<ref>{{cite web |title=Fatehpur Sikri |url=https://www.tajmahal.gov.in/fatehpur-sikri.aspx |website=www.tajmahal.gov.in |access-date=8 March 2022 |archive-date=8 March 2022 |archive-url=https://web.archive.org/web/20220308114239/https://www.tajmahal.gov.in/fatehpur-sikri.aspx |url-status=live }}</ref>ಚಕ್ರವರ್ತಿ ಅಕ್ಬರ್ 1571ಡ್ ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯೊದ ರಾಜಧಾನಿಯಾದ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಉಂಡ್ ಮಲ್ತೆರ್; ಉಂದು 1571 ಡ್ದ್ 1585 ಮುಟ್ಟ ಈ ಸ್ಥಾನಮಾನೊನು ಪಡೆಯೊಂದಿತ್ತ್ಂಡ್. ಪಂಜಾಬ್‌ಡ್ ನಡತಿನ ಒಂಜಿ ಅಭಿಯಾನೊಗು ಕಾರಣವಾದ್ ಅಕ್ಬರ್ ಉಂದೆನ್ ಬುಡ್ದು ಪೋಯೆರ್, ಬೊಕ್ಕ 1610 ಡ್ದ್ ಉಂದು ಪೂರ್ತಿಯಾದ್ ಕೈಬುಡಿಯೆರ್.<ref name="Routlegde">{{cite book |url=https://books.google.com/books?id=9A-EAgAAQBAJ&pg=PA82 |title=Dictionary of Islamic Architecture |author=Andrew Petersen |publisher=Routlegde |page=82 |isbn=9781134613656 |date=11 March 2002}}</ref> ಈ ಪೇಂಟೆದ ಪುದರ್ ದುಂಬು ಈ ಜಾಗೆನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಸಿಕ್ರಿ ಪನ್ಪಿನ ಊರುಡ್ದ್ ಬತ್ತ್ಂಡ್. ೧೯೯೯ಡ್ದ್ ೨೦೦೦ ಮುಟ್ಟ ಆರ್ಕಿಯೋಲೊಜಿಕಲ್ ಸರ್ವೇ ಆಫ್ ಇಂಡಿಯಾ (A.S.I.) ಕಾನಿಸ್‌ಮಾರಿ ಮಲ್ತಿನೆಟ್ಟ್ ಅಕ್ಬರ್ ರಾಜಧಾನಿನ್ ಕಟ್ಟೆರೆ ದುಂಬು ಮುಲ್ಪ ವಸತಿ, ದೇವಸ್ಥಾನೊಲು ಬೊಕ್ಕ ವಾಣಿಜ್ಯ ಕೇಂದ್ರೊಲು ಇತ್ತ್ಂಡ್ ಪನ್ಪಿನವು ತೋಜಿದ್ ಬತ್ತ್ಂಡ್. ಸುಂಗಾಸ್ ಪನ್ಪಿನಕ್ಲು ದುಂಬು ನೆಲೆ ಮಲ್ತೆರ್.<ref>{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=A. |last4=Kabui |first4=K. A. |title=Excavation at Birchhabili Tila, Sikri |journal=Puratattva: Journal of the Indian Archaeological Society |issue=31 |year=2001 |pages=55–56 |language=en}}</ref> ಕಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಆಪಿನ ಮುಟ್ಟ ಉಂದು ಸಿಕಾರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ನಿಯಂತ್ರನೊಡು ಇತ್ತ್ಂಡ್.<ref>{{cite book |last1=Patil |first1=Devendrakumar Rajaram |title=The Antiquarian Remains in Bihar |date=1963 |publisher=Kashi Prasad Jayaswal Research Institute |page=75 |url=https://books.google.com/books?id=9xNXAAAAMAAJ&q=sakarwar+rajputs+fatehpur+sikri |language=en}}</ref><ref name=":1" /> ಶೇಖ್ ಸಲೀಂ ಚಿಸ್ಟಿನ ಕಾನ್ಕಾ ದುಂಬು ಈ ಜಾಗೆಡ್ ಇತ್ತ್ಂಡ್. ಅಕ್ಬರ್ ನ ಮಗೆ, ಜಹಾಂಗೀರ್, 1569ಡ್ ಸಿಕ್ರಿ ಗ್ರಾಮೊಡು ಅಕ್ಬರೆನ ಮೋಕೆದ ಬುಡೆದಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಪುಟ್ಟುವೆರ್,<ref>{{cite book|last1=Hindu Shah|first1=Muhammad Qasim|title=Gulshan-I-Ibrahimi|page=223}}</ref> ಆ ವರ್ಷೊಡು, ಪುಟ್ಟುನೆನ್ ದುಂಬಾದ್ ಸೂಚನೆ ಮಲ್ತಿನ ಶೇಖ್‌ನ ನೆಂಪುಗಾದ್ ಒಂಜಿ ಧಾರ್ಮಿಕ ಗುಂಪುನು ಸುರು ಮಲ್ತೆರ್. ಜಹಾಂಗೀರ್‌ನ ರಡ್ಡನೆ ಪುಟ್ಟು ಪರ್ಬೊಗ್ ಅಕ್ಬರ್, ಒಂಜಿ ಗೋಡೆದ ನಗರ ಬೊಕ್ಕ ಸಾಮ್ರಾಜ್ಯಶಾಹಿ ಅರಮನೆದ ನಿರ್ಮಾಣೊನು ಸುರು ಮಲ್ತೆರ್. ೧೫೭೩ಡ್ ಅಕ್ಬರ್‌ನ ಗುಜರಾತ್‌ದ ವಿಜಯಶಾಲಿ ಪ್ರಚಾರೊದ ಬೊಕ್ಕ ಈ ನಗರೊನು ಫತೇಪುರ್ ಸಿಕ್ರಿ "ವಿಜಯದ ನಗರ" ಪನ್ಪಿನ ಪುದರ್‌‌ಡ್ ಗುರ್ತ ಮಲ್ತೆರ್. ೧೮೦೩ಡ್ ಆಗ್ರಾನ್ ಆಕ್ರಮಣ ಮಲ್ತಿನ ಬೊಕ್ಕ ಈಸ್ಟ್ ಇಂಡಿಯಾ ಕಂಪೆನಿ ಮುಲ್ಪ ಒಂಜಿ ಆಡಳಿತ ಕೇಂದ್ರ ಸ್ಥಾಪನೆ ಮಲ್ತ್ ದ್ ೧೮೫೦ ಮುಟ್ಟ ಇಂಚೆನೆ ಉಂತುಂಡು. ೧೮೧೫ಡ್, ಮಾರ್ಕ್ವೆಸ್ ಆಫ್ ಹ್ಯಾಸ್ಟಿಂಗ್ಸ್ ಸಿಕ್ರಿಡ್ ಸ್ಮಾರಕೊಲೆನ್ ದುರಸ್ತಿ ಮಲ್ಪೆರೆ ಆದೇಶ ಮಲ್ತೆರ್. ಮೊಘಲ್ ಸಾಮ್ರಾಜ್ಯದ ರಾಜಧಾನಿ ಆದ್ ಐತಿಹಾಸಿಕ ಮಹತ್ವ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪಗ್[೧] ಕಾರಣವಾಯಿನ, ಫತೇಪುರ್ ಸಿಕ್ರಿಗ್ 1986ಡ್ ಯುನೆಸ್ಕೋದ ವಿಶ್ವ ಪರಂಪರೆದ ತಾನೊದ ಸ್ಥಾನಮಾನೊನು ತಿಕ್ಕುಂಡ್.<ref>{{cite web |title=Fatehpur Sikri |url=https://whc.unesco.org/en/list/255/ |website=UNESCO World Heritage Centre |access-date=15 February 2022 |language=en |archive-date=15 February 2022 |archive-url=https://web.archive.org/web/20220215162920/https://whc.unesco.org/en/list/255/ |url-status=live }}</ref><ref>{{Cite web|url = https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri/ |title = 30 Years as World Heritage Site: Fatehpur Sikri|date = 22 March 2016|access-date = 13 January 2022|archive-date = 13 January 2022|archive-url = https://web.archive.org/web/20220113152418/https://www.makeheritagefun.com/30-years-world-heritage-site-fatehpur-sikri |url-status=live}}</ref> ==ಇತಿಹಾಸೊ== ಪುರಾತತ್ತ್ವ ಶಾಸ್ತ್ರೊದ ಸಾಕ್ಷಿದ ಪ್ರಕಾರ ಪೇಂಟೆಡ್ ಗ್ರೇ ವೇರ್ ಕಾಲೊಡ್ದ್ ಬೊಕ್ಕ ಈ ಜಾಗೆಡ್ ಜನಜೀವನ ಇತ್ತ್ಂಡ್ ಪನ್ಪುನ ತೋಜಿದ್ ಬೈದ್ಂಡ್. ಇತಿಹಾಸಕಾರೆ ಸೈಯದ್ ಅಲಿ ನದೀಮ್ ರೆಜಾವಿನ ಪ್ರಕಾರ, ಈ ಪ್ರದೇಶ ಸುಂಗಾ ಆಡಳಿತೊಡು ಬೊಕ್ಕ ಸಿಕರ್ವಾರ್ ರಾಜಪುತ್ರೆರೆನ ಆಡಳಿತೊಡು ಬುಲೆಂಡ್, ಅಕುಲು 7ನೆಡ್ದ್ 16ನೆ ಶತಮಾನೊಗು ಖಾನ್ವಾ ಯುದ್ಧ (೧೫೨೭) ಮುಟ್ಟ ಈ ಪ್ರದೇಶೊನು ನಿಯಂತ್ರಣ ಮಲ್ಪುನಗ ಒಂಜಿ ಕೋಟೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಪ್ರದೇಶೊ ಬೊಕ್ಕ ದೆಹಲಿ ಸುಲ್ತಾನತೆದ ಆಡಳಿತೊಗು ಬತ್ತ್ಂಡ್ಬೊ.ಖಾಲ್ಜಿ ರಾಜವಂಶೊದ ಕಾಲೊಡು ಗಾತ್ರೊಡು ಬುಲೆಯಿನ ಜಾಗೆಡ್ ಮಸ್ತ್ ಮಸೀದಿಲೆನ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name=":1">{{cite book |last1=Rezavi |first1=Syed Ali Nadeem |title=Sikri before Akbar |date=2013 |publisher=Oxford University Press |isbn=978-0-19-908256-8 |url=https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |access-date=13 January 2020 |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113124948/https://www.oxfordscholarship.com/view/10.1093/acprof:oso/9780198084037.001.0001/acprof-9780198084037-chapter-2 |url-status=live }}</ref><ref>{{cite news |last1=Safvi |first1=Rana |title=The secrets about Fatehpur Sikri |url=https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |access-date=13 January 2020 |work=The Hindu |date=10 December 2017 |language=en-IN |archive-date=13 January 2020 |archive-url=https://web.archive.org/web/20200113125020/https://www.thehindu.com/opinion/columns/the-secrets-about-fatehpur-sikri/article21381895.ece |url-status=live }}</ref> ==ಮುಘಲೆರ್ದ್ ದುಂಬುದ ಕಾಲ== ದುಂಬು ಇತ್ತಿನ ಕೆದುತ ಆಗ್ನೇಯ ತೀರೊಡು ಉಪ್ಪುನ ಬಿರ್ಛಾಬಿಲಿ ತಿಲಾಡ್ ನಡತಿನ ಸರ್ವೆಡ್ ಕ್ರಿ.ಶ.೬ನೆ ಶತಮಾನೊಗು ದುಂಬು ಒಂಜಿ ವಸಾಹತು ಇತ್ತ್ಂಡು ಪನ್ಪುನ ಪಳಯುಲಿಕೆಲ್ ತಿಕ್ಕುಂಡು.<!-- Sharma et al. 2001, pp. 56-7 --> ೬ನೆಡ್ದ್ ೯ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನ ಪಿರಾಕ್ ದ ಪದರೊಡು ಇಟ್ಟಿಗೆದ ಗೋಡೆಲು, ಕಟ್ಟಡೊಲೆನ ಅಡಿಪಾಯೊಲು, ಒಂಜಿ ಆಕಾರೊದ ಕಲ್ಲುದ ಸ್ತಂಭ, ಬೊಕ್ಕ ಕೆಂಪು ಸ್ಯಾಂಡ್‌ಸ್ಟೋನ್‌ಡ್<!-- Sharma et al. 2001, p. 57 --> ಮಲ್ತಿನ ನರಮಾನಿಲೇ ಮಲ್ತಿನ ಗುಂಡಿ ಸೇರ್ದ್ ಬೇತೆ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅವಶೇಸೋಲು ಉಂಡು. ಜೈನ ದೇವತೆ ಅಂಬಿಕಾನ ತುಂಡಾತಿನ ಶಿಲ್ಪ ಶೈಲಿಶಾಸ್ತ್ರೀಯವಾದ್ 6ನೆ ಅತ್ತಂದೆ 7ನೆ ಶತಮಾನೊಗು ಸೇರ್ದಿನವು, ಕಲ್ಲ್ ದ ಸ್ತಂಭ ಬೊಕ್ಕ ಬ್ರಾಹ್ಮೀ ಲಿಪಿಟ್ ರಡ್ಡ್ ಅಕ್ಷರೊದ ಶಾಸನೊ, ಈ ಕಾಲೊಡು ಜೈನ ದೇಗುಲೊದ ಇತ್ತಿನೆನ್ ತೋಜಾವುಂಡು.<!-- Sharma et al. 2001, p. 58 --> ನನಾತ್ ಅಗೆತಿನೆಟ್ಟ್ ಬತ್ತಿನ ಬೇತೆ ಕುಂಬಾರಿಕೆದ ತುಂಡುಲು, ಗೊಬ್ಬುನೆತ್ತ ತುಂಡುಲು, ಬರವುದ ವಸ್ತುಲು, ಅಚ್ಚುಲು, ಕುಪ್ಪಿದ ಬೊಕ್ಕ ಕರ್ಬೊದ ಕಾಜಿಲು, ಬೊಕ್ಕ ಒಂಜಿ ಈಟಿ ಪೂರಾ ತಿಕ್ಕುದಿತ್ತ್ಂಡು.<!-- Sharma et al. 2001, p. 58 --><ref name="Sharma et al. 2001">{{cite journal |last1=Sharma |first1=D. V. |last2=Prabhakar |first2=V. N. |last3=Pradhan |first3=V. |last4=Kabui |first4=K. I. |title=Excavation at Birchhabili Tila, Sikri |journal=Purātattva |date=2001 |volume=31 |pages=55–72 |url=https://www.academia.edu/43373675/Excavation_at_Birchhabili_Tila_Sikri |access-date=7 March 2026}}</ref> ==ಭಾಸೆ== ೨೦೧೧ ರ ಜನಗಣತಿ ಪ್ರಕಾರ ೯೮.೮೧% ಜನಕುಲು ಹಿಂದಿ ಪಾತೆರುನಕುಲು ಬೊಕ್ಕ ೧.೦೪% ಜನಕುಲು ಬ್ರಜ್ ಭಾಷಾ ಪಾತೆರುನಕುಲು ಪಂಡ್‌ದ್ ತೆರಿದುಂಡು.<ref>{{cite web |title=C-16 Population By Mother Tongue - Town level |url=https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |website=censusindia.gov.in |access-date=2 April 2021 |archive-date=28 July 2021 |archive-url=https://web.archive.org/web/20210728041052/https://censusindia.gov.in/2011census/C-16T/DDW-C16-TOWN-STMT-MDDS-0900.XLSX |url-status=live }}</ref> ==ಸರಕಾರ ಬೊಕ್ಕ ರಾಜಕೀಯ== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಆಗ್ರಾ ಜಿಲ್ಲೆದ ಪದಿನೈನ್ ಬ್ಲಾಕ್‌ದ ಮುಖ್ಯ ಕಚೇರಿಲೆಡ್ ಒಂಜಿ. ಉಂದೆತ ಅಡಿಟ್ ೫೨ ಗ್ರಾಮ ಪಂಚಾಯತ್‌ ಉಂಡು. ಫತೇಪುರ್ ಸಿಕ್ರಿ, ಲೋಕಸಭೆದ ಒಂಜಿ ಕ್ಷೇತ್ರ, ಭಾರತೀಯ ಸಂಸತ್‌ ದ ಲೋಕ ಸಬೆಟ್ (Lower house), ಬೊಕ್ಕ ಐನ್ ವಿಧಾನಸಭೆ ( ಶಾಸಕಾಂಗ ಸಭೆ ) ವಿಭಾಗೊಲೆನ್ ಸೇರ್‌ದುಂಡು: ಆಗ್ರಾ ಗ್ರಾಮೀಣ | ಫತೇಪುರ ಸಿಕ್ರಿ ಖೇರಾಗಢ್ ಫತೇಹಾಬಾದ್ ಬಾಹ್ ==ವಾಸ್ತುಶಿಲ್ಪಲು== [[File:General Plan of Fatehpur Sikri City India 1917.jpg|thumb|250px|೧೯೧೭ನೆತ್ತ ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಸಾಮಾನ್ಯ ಯೋಜನೆ.]] ಫತೇಪುರ್ ಸಿಕ್ರಿ ಒಂಜಿ ಕಲ್ಲುದ ಗುಡ್ಡೆಡ್ ಉಂಡು, 3 kilometres (1.9 mi) ಉದ್ದೊಡು ಬೊಕ್ಕ 1 km (0.62 mi) ಅಗಲ, ಬೊಕ್ಕ ಅರಮನೆದ ನಗರೊನು 6 km (3.7 mi) ಡ್ದ್ ಸುತ್ತಲಾ ಉಂಡು ಮೂಜಿ ಬದಿಟ್ ಗೋಡೆ ನಾಲ್ನೆ ಕೆದುತ ಗಡಿಟ್ ಉಂಡು. ಸಾಮಾನ್ಯವಾದ್ ಈ 40 ಮೀ ಎತ್ತರದ ಗುಡ್ಡೆದ ಸುತ್ತ ಏರ್ಪಾಟ್ ಆತ್ಂಡ್, ಬೊಕ್ಕ ಉಂದು ಸರಿಸುಮಾರು ಒಂಜಿ ರೊಂಬಸ್ ಆಕಾರೊಡು ಬೂರುಂಡು. ನೆಲತ ರಚನೆಲೆನ ಸಾಮಾನ್ಯ ವಿನ್ಯಾಸ, ವಿಶೇಷವಾದ್ "ತೋಟೊಲೆನ ನಿರಂತರ ಬೊಕ್ಕ ಕಾಂಪ್ಯಾಕ್ಟ್ ನಮೂನೆ ಬೊಕ್ಕ ಸೇವೆಲು ಬೊಕ್ಕ ಸೌಲಭ್ಯೊಲು" ನಗರೊನು ಗುರ್ತಮಲ್ಪೆರೆ ಆಪುಂಡು. ನಗರ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆನ್ ಫತೇಪುರ್ ಸಿಕ್ರಿನ್ ಮುಖ್ಯವಾದ್ ಅಯಿತ ಪ್ರಸಿದ್ಧ ನಿವಾಸಿಲೆಗ್ ವಿರಾಮ ಬೊಕ್ಕ ಐಷಾರಾಮಿ ಸೌಲಭ್ಯೊಲೆನ್ ಕೊರ್ಪುನಂಚ ಕಟ್ಟ್‌ದೆರ್ ಪನ್ಪಿನ ತೀರ್ಮಾನೊಗು ಕಾರಣವಾತ್ಂಡ್.. ಫತೇಹ್ಪುರ್ ಸಿಕ್ರಿದ ರಾಜವಂಶದ ವಾಸ್ತುಶಿಲ್ಪೊನು ಟಿಮುರಿಡ್ ರೂಪೊಲು ಬೊಕ್ಕ ಶೈಲಿಲೆನ್ ಆಧಾರವಾದ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Islam: Art and Architecture|author=Markus Hattstein, Peter Delius|publisher=Könemann|year=2000|pages=466}}</ref> The city was built massively and preferably with red sandstone.<ref>{{cite book|url=https://books.google.com/books?id=P2R3a3KLl2IC&pg=PA1|title=Mughal Architecture|author=Moritz Herrmann|publisher=GRIN Verlag|year=2011|page=3|isbn=9783640930036}}</ref> ನಗರೊನು ಮಸ್ತ್ ಮಟ್ಟ್‌ಡ್ ಬೊಕ್ಕ ಕೆಂಪು ಮರಳುಕಲ್ಲ್ ಡ್ ಮಲ್ತೆರ್.<ref>{{cite book|title=Mughal Architecture: An Outline of Its History and Development (1526-1858)|author=Ebba Koch|publisher=Prestel|year=1991|page=60|author-link=Ebba Koch}}</ref> ಫತೇಪುರ್ ಸಿಕ್ರಿದ ಅರಮನೆಲೆನ ವಾಸ್ತುಶಿಲ್ಪದ ಶಬ್ದಕೋಶೊಡು ಬೊಕ್ಕ ಅಲಂಕಾರೊಡುಲಾ ಗುಜರಾತಿದ ಪ್ರಭಾವೊಲು ತೋಜುಂಡು. <ref>{{cite book|url=https://books.google.com/books?id=3ctLNvx68hIC&pg=PA50|title=Architecture of Mughal India, Part 1, Volume 4|author=Catherine Ella Blanshard Asher|publisher=Cambridge University Press|year=1992|page=50|isbn=9780521267281}}</ref> ನಗರದ ವಾಸ್ತುಶಿಲ್ಪ ಆ ಕಾಲೊಡು ಭಾರತೊಡು ಜನಪ್ರಿಯವಾದಿತ್ತಿನ ದೇಶೀಯ ವಾಸ್ತುಶಿಲ್ಪದ ಹಿಂದೂ ಬೊಕ್ಕ ಮುಸ್ಲಿಂ ರೂಪೊಲೆನ್ ಪ್ರತಿಬಿಂಬಿಸವುಂಡು. ಈ ಮೂಲ ಜಾಗೊಲೆನ ಗಮನಾರ್ಹ ಸಂರಕ್ಷಣೆ ಆಧುನಿಕ ಪುರಾತತ್ತ್ವಜ್ಞೆರೆಗ್ ಮೊಘಲ್ ನ್ಯಾಯಾಲಯದ ಜೀವನದ ದೃಶ್ಯೊಲೆನ್ ಪಿರ ಕಟ್ಟೆರೆ ಸುಲಭ ಮಲ್ಪುಂಡು, ಬೊಕ್ಕ ನಗರದ ರಾಜವಂಶದ ಬೊಕ್ಕ ಶ್ರೀಮಂತ ನಿವಾಸಿಲೆನ ಶ್ರೇಣಿ ವ್ಯವಸ್ಥೆನ್ ಎಡ್ಡೆ ರೀತಿಡ್ ಅರ್ಥ ಮಲ್ಪುಂಡು. 5 miles (8.0 km) ದೂರೊಡು ಉಪ್ಪುನ ಗೇಟ್ ಲೆಡ್ದ್ ಉಂದೆನ್ ಪೊಗ್ಗೊಲಿ ಉದ್ದದ ಕೋಟೆ ಗೋಡೆ, ಅವು, ದೆಹಲಿ ದ್ವಾರ, ಲಾಲ್ ದ್ವಾರ, ಆಗ್ರಾ ದ್ವಾರ ಬೊಕ್ಕ ಬಿರ್ಬಲ್ ದ್ವಾರ, ಚಂದನ್ಪಾಲ್ ದ್ವಾರ, ಗ್ವಾಲಿಯರ್ ದ್ವಾರ, ತೆಹ್ರಾ ದ್ವಾರ, ಚೋರ್ ದ್ವಾರ, ಬೊಕ್ಕ ಅಜ್ಮೇರಿ ದ್ವಾರ. ಈ ಅರಮನೆಡ್ ರಾಣಿ ಮರಿಯಮ್-ಉಜ್-ಜಮಾನಿಗ್ ಬೇಸಿಗೆದ ಅರಮನೆ ಬೊಕ್ಕ ಚಳಿಗಾಲದ ಅರಮನೆ ಉಂಡು, ಉಂದೆನ್ ಸಾಮಾನ್ಯವಾದ್ ಜೋಧಾ ಬಾಯಿ ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್. [[File:Jama Masjid-Sikri-Fatehpur Sikri-India0008.JPG|thumb|250px|[[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Fatehpur Sikiri Salim Chishti Tomb 2010.jpg|thumb|250px|[[ಸಲೀಂ ಚಿಸ್ಟಿನ ಗೋರಿ]] in [[ಜಮಾ ಮಸೀದಿ, ಫತೇಪುರ ಸಿಕ್ರಿ|ಜಮಾ ಮಸೀದಿ]] ಕೋರ್ಟ್‌‌ಯಾರ್ಡ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Panch Mahal-Fatehpur-Fatehpur Sikri India0014.JPG|thumb|250px|[[ಪಂಚ್ ಮಹಲ್, ಫತೇಪುರ ಸಿಕ್ರಿ]]]] [[File:Hiran Minar FatehpurSikri.jpg|thumb|ಹಿರನ್ ಮಿನಾರ್, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Fatehpur Sikri Fort 166.jpg|thumb|Rear view of the grand gateway of [[ಜೋದಾ ಬಾಯಿ ಮಹಲ್]], ಫತೇಪುರ ಸಿಕ್ರಿಡ್ ಮಲ್ಲ ಜನಮಾನಿ ಇತ್ತಿನ ಮಹಲ್]] [[File:Monuments in Fatehpur Sikri.jpg|thumb|ಸ್ಮಾರಕೊಲು, ಫತೇಪುರ ಸಿಕ್ರಿ]] [[File:Rear View of Jodha Bai Palace, Fatehpur Sikri.jpg|thumb|ಜೋದ್ ಬಾಯಿ ಮಹಲ್‌ದ ಕೈತಲ್‌ದ ಚಿತ್ರನೊ, ಫತೇಪುರ ಸಿಕ್ರಿ]] ==ಸಾಗಾವುನಿ (ಬಂಡಿ)== ಫತೇಪುರ್ ಸಿಕ್ರಿ ಸುಮಾರ್ 37 kilometres (23 mi) ಆಗ್ರಾ‌ಡ್ದ್.<ref>{{Cite encyclopedia |title=Fatehpur Sikri - Architecture, Foundation, & History |url=https://www.britannica.com/place/Fatehpur-Sikri |encyclopedia=Britannica |date=28 December 2024 |access-date=1 January 2025}}</ref> ಕೈತಲ್‌ದ ವಿಮಾನ ನಿಲ್ದಾಣ ಆಗ್ರಾ ವಿಮಾನ ನಿಲ್ದಾಣ ( ಖೇರಿಯಾ ವಿಮಾನ ನಿಲ್ದಾಣ ಪಂಡ್ದ್ ಲಾ ಲೆಪ್ಪುವೆರ್), 40 kilometres (25 mi) ಫತೇಪುರ್ ಸಿಕ್ರಿ‌ಡ್ದ್. ಕೈತಲ್‌ದ ರೈಲ್ವೆ ನಿಲ್ದಾಣ ಫತೇಪುರ್ ಸಿಕ್ರಿ ರೈಲ್ವೆ ನಿಲ್ದಾಣ, ಸುಮಾರ್ 1 kilometre (0.62 mi) ನಗರ ಕೇಂದ್ರೊಡ್ದ್. ಉಂದು ಆಗ್ರಾ ಬೊಕ್ಕ ನೆರೆಕರೆತ ಕೇಂದ್ರೊಲೆಗ್ ರಸ್ತೆಡ್ ಸಂಪರ್ಕೊಡು ಉಂಡು, ಅಲ್ಪ ಪ್ರವಾಸಿ ಬಸ್ಸುಲು ಬೊಕ್ಕ ಟ್ಯಾಕ್ಸಿಲೆನ ಒಟ್ಟಿಗೆಯುಪಿಎಸ್ಆರ್ಟಿಸಿ ನಿಯಮಿತ ಬಸ್ ಸೇವೆಲೆನ್ ಕೊರ್ಪುಂಡು. ==ಜನಪ್ರಿಯ ಸಂಸ್ಕೃತಿಡ್== ಸಲ್ಮಾನ್ ರುಶ್ದಿ ನ ಕಾದಂಬರಿಡ್ ದಿ ಎನ್ಚಾಂಟ್ರೆಸ್ ಆಫ್ ಫ್ಲೋರೆನ್ಸ್ 16ನೆ ಶತಮಾನೊದ ಫತೇಪುರ್ ಸಿಕ್ರಿಡ್ ನಡತ್ಂಡ್. <ref>{{cite news |last1=Choudhury |first1=Chandrahas |title=An ode to adolescent fantasy |url=https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |access-date=27 March 2022 |work=Mint |date=18 April 2008 |language=en |archive-date=27 March 2022 |archive-url=https://web.archive.org/web/20220327194048/https://www.livemint.com/Leisure/3yinn88DBXtIWFTrqJaAAK/An-ode-to-adolescent-fantasy.html |url-status=live }}</ref> ==ಸಿನಿಮಾಲೆಡ್== ಶಾರುಖ್ ಖಾನ್ ಬೊಕ್ಕ ಮಹಿಮಾ ಚೌಧರಿ ನಟನೆ ಮಲ್ತಿನ ಹಿಂದಿ ಚಿತ್ರ ಪರ್ದೇಸ್‌ಡ್ದ್ ೧೯೯೭ಡ್ ಹಿಟ್ ಆಯಿನ "ದೋ ದಿಲ್ ಮಿಲ್ ರಾಹೇ ಹೈನ್" ಪನ್ಪಿನ ಪದನ್ ನಗರೊಡು ಚಿತ್ರೀಕರಣ ಮಲ್ತೆರ್.<ref>{{Cite web |url=https://www.youtube.com/watch?v=gmJW33derRY |title=Do Dil Mil Rahe Hain from movie Pardes 1997|date=4 March 2022 |publisher=YouTube}}</ref> ==ಉಲ್ಲೇಕ== {{Reflist}} 174jions1b48tx2d4cd0jxgnvex44ru ಟೆಂಪ್ಲೇಟ್:Infobox UNESCO World Heritage Site 10 28032 366107 2026-07-25T03:28:38Z en>Zackmann08 0 removing support for deprecated parameters that have been cleaned. 366107 wikitext text/x-wiki <includeonly>{{main other|{{#if:{{Has short description}} |<!--Do nothing--> |{{#invoke:Type in location|main|UNESCO World Heritage Site|{{{location|}}}}}}}}}{{#if:{{{child|}}}|<templatestyles src="Infobox/mobileviewfix.css"/>}} {{Infobox | child = {{lc:{{{child|}}}}} | decat = yes<!-- remove from tracking category --> |bodyclass = vcard |titleclass = category |titlestyle = font-size:125%; |title = {{#if:{{{child|}}}||{{if empty|{{{name|}}}|{{PAGENAMEBASE}}}}}} |aboveclass = fn org |abovestyle = padding:0.2em;background:#87CEEB; color:inherit;font-size:100%; |above = [[UNESCO World Heritage Site]] |imagestyle = border-top:1px #aaa solid;padding-top:0.4em; |image = {{#invoke:InfoboxImage|InfoboxImage|image={{{image|}}}|size={{{image_size|}}}|upright={{if empty|{{{image_upright|}}}|1}}|alt={{{alt|}}}}} |caption = {{{caption|}}} |image2 = {{#invoke:Location map|infobox_pushpin_map | default_width = 220 | max_width = 250 }} |image3 ={{#invoke:InfoboxImage|InfoboxImage|image={{{image_map|}}}|size={{{map_size|}}}|maxsize=256px|alt={{{map_alt|}}}}} |caption3 = {{{map_caption|}}} |image4 = {{#ifeq:{{yesno-no|{{{hide_interactive_map|}}}|}}|yes||{{#invoke:Infobox mapframe | autoWithCaption | onByDefault = {{#if:{{{pushpin_map|}}}{{{image_map|}}}{{{child|}}}|no|yes}} | mapframe-caption = Interactive map of {{if empty|{{{name|}}}|{{PAGENAMEBASE}}}} }}}} | rowclass1 = {{#if:{{{child|}}}|mergedrow|}} | header1 = {{#if:{{{child|}}}|{{#invoke:Designation|divbox|WHS}}|}} |labelstyle = padding-right:0.3em; | rowclass2 = mergedrow | label2 = Official&nbsp;name | data2 = {{{official_name|}}} | rowclass3 = mergedrow | label3 = [[World Heritage Sites by country|Location]] | data3 = {{{location|}}} | rowclass4 = mergedrow | label4 = Part of | data4 = {{{part_of|}}} | rowclass5 = mergedrow | label5 = Includes | data5 = {{{includes|}}} | rowclass6 = mergedrow | label6 = [[World Heritage Site#Selection criteria|Criteria]] | class6 = category | data6 = {{#if: {{{type|}}} |{{{type|}}}:&nbsp;| }}{{{criteria|}}} | rowclass7 = mergedrow | label7 = Reference | data7 = {{#if:{{{ID|}}} |{{#if:{{{link|}}} |[{{{link|}}} {{{ID}}}] |[https://whc.unesco.org/en/list/{{{ID}}} {{{ID}}}] }} }} | rowclass8 = mergedrow | label8 = Inscription | data8 = {{if first display both|{{{year|}}}| (<!-- -->{{#if:{{{session|}}} | {{{session|}}} | {{#ifexpr:{{{year|}}}>1976 | {{#ifexpr:{{{year|}}}>2020<!--Not held in 2020 due to COVID-19 pandemic--> | {{#ifexpr:{{{year|}}}>2022<!--Not held in 2022 due to Russian invasion of Ukraine--> | {{ordinal|{{#expr:{{{year|}}}-1978}}}} | {{ordinal|{{#expr:{{{year|}}}-1977}}}}}} | {{ordinal|{{#expr:{{{year|}}}-1976}}}}}} | Unknown}} <!-- -->}} [[World Heritage Committee|Session]])<!-- -->}} | rowclass9 = mergedrow | label9 = Extensions | data9 = {{{extension|}}} | rowclass10 = mergedrow | label10 = [[List of World Heritage in Danger|Endangered]] | data10 = {{{danger|}}} | rowclass11 = mergedrow | label11 = Area | class11 = category | data11 = {{{area|}}} | rowclass12 = mergedrow | label12 = Buffer&nbsp;zone | class12 = category | data12 = {{{buffer_zone|}}} | label13 = Website | class13 = category | data13 = {{{website|}}} | label14 = Coordinates | data14 = {{{coordinates|}}} | data15 = {{{embedded|}}} | label16 = Public transit access | data16 = {{{public_transit|}}} }}</includeonly>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox UNESCO World Heritage Site with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox UNESCO World Heritage Site]] with unknown parameter "_VALUE_"|ignoreblank=y|mapframe_args=y|pushpin_map_args=y| alt | area | buffer_zone | caption | child | coordinates | criteria | danger | embedded | extension | hide_interactive_map | ID | image | image_map | image_size | image_upright | includes | link | location | map_alt | map_caption | map_size | name | official_name | part_of | public_transit | session | type | website | year }}<noinclude> {{documentation}}<!--Please add any categories to the /doc page --> </noinclude> plyvnryx1m3dxkdb73195damk05zjjy 366108 366107 2026-08-21T02:53:24Z ChiK 1136 ೧ revisions imported from [[:en:Template:Infobox_UNESCO_World_Heritage_Site]] 366107 wikitext text/x-wiki <includeonly>{{main other|{{#if:{{Has short description}} |<!--Do nothing--> |{{#invoke:Type in location|main|UNESCO World Heritage Site|{{{location|}}}}}}}}}{{#if:{{{child|}}}|<templatestyles src="Infobox/mobileviewfix.css"/>}} {{Infobox | child = {{lc:{{{child|}}}}} | decat = yes<!-- remove from tracking category --> |bodyclass = vcard |titleclass = category |titlestyle = font-size:125%; |title = {{#if:{{{child|}}}||{{if empty|{{{name|}}}|{{PAGENAMEBASE}}}}}} |aboveclass = fn org |abovestyle = padding:0.2em;background:#87CEEB; color:inherit;font-size:100%; |above = [[UNESCO World Heritage Site]] |imagestyle = border-top:1px #aaa solid;padding-top:0.4em; |image = {{#invoke:InfoboxImage|InfoboxImage|image={{{image|}}}|size={{{image_size|}}}|upright={{if empty|{{{image_upright|}}}|1}}|alt={{{alt|}}}}} |caption = {{{caption|}}} |image2 = {{#invoke:Location map|infobox_pushpin_map | default_width = 220 | max_width = 250 }} |image3 ={{#invoke:InfoboxImage|InfoboxImage|image={{{image_map|}}}|size={{{map_size|}}}|maxsize=256px|alt={{{map_alt|}}}}} |caption3 = {{{map_caption|}}} |image4 = {{#ifeq:{{yesno-no|{{{hide_interactive_map|}}}|}}|yes||{{#invoke:Infobox mapframe | autoWithCaption | onByDefault = {{#if:{{{pushpin_map|}}}{{{image_map|}}}{{{child|}}}|no|yes}} | mapframe-caption = Interactive map of {{if empty|{{{name|}}}|{{PAGENAMEBASE}}}} }}}} | rowclass1 = {{#if:{{{child|}}}|mergedrow|}} | header1 = {{#if:{{{child|}}}|{{#invoke:Designation|divbox|WHS}}|}} |labelstyle = padding-right:0.3em; | rowclass2 = mergedrow | label2 = Official&nbsp;name | data2 = {{{official_name|}}} | rowclass3 = mergedrow | label3 = [[World Heritage Sites by country|Location]] | data3 = {{{location|}}} | rowclass4 = mergedrow | label4 = Part of | data4 = {{{part_of|}}} | rowclass5 = mergedrow | label5 = Includes | data5 = {{{includes|}}} | rowclass6 = mergedrow | label6 = [[World Heritage Site#Selection criteria|Criteria]] | class6 = category | data6 = {{#if: {{{type|}}} |{{{type|}}}:&nbsp;| }}{{{criteria|}}} | rowclass7 = mergedrow | label7 = Reference | data7 = {{#if:{{{ID|}}} |{{#if:{{{link|}}} |[{{{link|}}} {{{ID}}}] |[https://whc.unesco.org/en/list/{{{ID}}} {{{ID}}}] }} }} | rowclass8 = mergedrow | label8 = Inscription | data8 = {{if first display both|{{{year|}}}| (<!-- -->{{#if:{{{session|}}} | {{{session|}}} | {{#ifexpr:{{{year|}}}>1976 | {{#ifexpr:{{{year|}}}>2020<!--Not held in 2020 due to COVID-19 pandemic--> | {{#ifexpr:{{{year|}}}>2022<!--Not held in 2022 due to Russian invasion of Ukraine--> | {{ordinal|{{#expr:{{{year|}}}-1978}}}} | {{ordinal|{{#expr:{{{year|}}}-1977}}}}}} | {{ordinal|{{#expr:{{{year|}}}-1976}}}}}} | Unknown}} <!-- -->}} [[World Heritage Committee|Session]])<!-- -->}} | rowclass9 = mergedrow | label9 = Extensions | data9 = {{{extension|}}} | rowclass10 = mergedrow | label10 = [[List of World Heritage in Danger|Endangered]] | data10 = {{{danger|}}} | rowclass11 = mergedrow | label11 = Area | class11 = category | data11 = {{{area|}}} | rowclass12 = mergedrow | label12 = Buffer&nbsp;zone | class12 = category | data12 = {{{buffer_zone|}}} | label13 = Website | class13 = category | data13 = {{{website|}}} | label14 = Coordinates | data14 = {{{coordinates|}}} | data15 = {{{embedded|}}} | label16 = Public transit access | data16 = {{{public_transit|}}} }}</includeonly>{{#invoke:Check for unknown parameters|check|unknown={{main other|[[Category:Pages using infobox UNESCO World Heritage Site with unknown parameters|_VALUE_{{PAGENAME}}]]}}|preview=Page using [[Template:Infobox UNESCO World Heritage Site]] with unknown parameter "_VALUE_"|ignoreblank=y|mapframe_args=y|pushpin_map_args=y| alt | area | buffer_zone | caption | child | coordinates | criteria | danger | embedded | extension | hide_interactive_map | ID | image | image_map | image_size | image_upright | includes | link | location | map_alt | map_caption | map_size | name | official_name | part_of | public_transit | session | type | website | year }}<noinclude> {{documentation}}<!--Please add any categories to the /doc page --> </noinclude> plyvnryx1m3dxkdb73195damk05zjjy ಟೆಂಪ್ಲೇಟ್:Incomplete list 10 28033 366109 2025-10-13T08:52:00Z en>Rich Farmbrough 0 Allow text variation. 366109 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B={{ambox |small = left |style = width: auto; |textstyle = width: auto; |text = {{#switch: {{{type|{{{1|}}}}}} | alumni = This alumni, faculty and related people list{{#if:{{NAMESPACE}}||[[Category:Incomplete alumni lists]]}} | Film | film = This film-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete film lists]]}} | literature = This literature-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete literature lists]]}} | music = This list of songs or music-related items{{#if:{{NAMESPACE}}||[[Category:Incomplete music lists]]}} | personnel = This musical group's list of members{{#if:{{NAMESPACE}}||[[Category:Musical group articles with incomplete personnel listings]]}} | sportresults = This list of sports fixtures or results{{#if:{{NAMESPACE}}||[[Category:Incomplete sports result lists]]}} | science = This science-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete science lists]]}} | sport = This sports-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete sports lists]]}} | transport = This transport-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete transport lists]]}} | television = This television-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete television lists]]}} | video game | videogames = This list related to video games{{#if:{{NAMESPACE}}||[[Category:Incomplete video game lists]]}} | video = This list related to film, television, or video{{#if:{{NAMESPACE}}||[[Category:Incomplete film, television, or video lists]]}} |This list}} is [[Wikipedia:WikiProject Lists#Incomplete lists|incomplete]]; you can help by [{{fullurl:{{FULLPAGENAME}}|action=edit}} adding missing {{{items|items}}}]. |date = {{{date|}}} }}{{main other|{{TDMCA|Incomplete lists|from|{{{date|}}}}}}} }}<noinclude> {{documentation}} </noinclude> pb33mhfx9ikrob8vh1ys69ganojoxf0 366110 366109 2026-08-21T02:53:25Z ChiK 1136 ೧ revisions imported from [[:en:Template:Incomplete_list]] 366109 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B={{ambox |small = left |style = width: auto; |textstyle = width: auto; |text = {{#switch: {{{type|{{{1|}}}}}} | alumni = This alumni, faculty and related people list{{#if:{{NAMESPACE}}||[[Category:Incomplete alumni lists]]}} | Film | film = This film-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete film lists]]}} | literature = This literature-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete literature lists]]}} | music = This list of songs or music-related items{{#if:{{NAMESPACE}}||[[Category:Incomplete music lists]]}} | personnel = This musical group's list of members{{#if:{{NAMESPACE}}||[[Category:Musical group articles with incomplete personnel listings]]}} | sportresults = This list of sports fixtures or results{{#if:{{NAMESPACE}}||[[Category:Incomplete sports result lists]]}} | science = This science-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete science lists]]}} | sport = This sports-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete sports lists]]}} | transport = This transport-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete transport lists]]}} | television = This television-related list{{#if:{{NAMESPACE}}||[[Category:Incomplete television lists]]}} | video game | videogames = This list related to video games{{#if:{{NAMESPACE}}||[[Category:Incomplete video game lists]]}} | video = This list related to film, television, or video{{#if:{{NAMESPACE}}||[[Category:Incomplete film, television, or video lists]]}} |This list}} is [[Wikipedia:WikiProject Lists#Incomplete lists|incomplete]]; you can help by [{{fullurl:{{FULLPAGENAME}}|action=edit}} adding missing {{{items|items}}}]. |date = {{{date|}}} }}{{main other|{{TDMCA|Incomplete lists|from|{{{date|}}}}}}} }}<noinclude> {{documentation}} </noinclude> pb33mhfx9ikrob8vh1ys69ganojoxf0 ಮೋಡ್ಯೂಲ್:Location map/data/Germany 828 28034 366113 2016-07-19T19:49:10Z en>MSGJ 0 update location map, per request 366113 Scribunto text/plain return { name = 'Germany', top = 55.1, bottom = 47.2, left = 5.5, right = 15.5, image = 'Germany adm location map.svg', image1 = 'Relief Map of Germany.svg' } n5s7b19l0ces1totgx6a9w7xuyv5kag 366114 366113 2026-08-21T02:53:27Z ChiK 1136 ೧ revisions imported from [[:en:Module:Location_map/data/Germany]] 366113 Scribunto text/plain return { name = 'Germany', top = 55.1, bottom = 47.2, left = 5.5, right = 15.5, image = 'Germany adm location map.svg', image1 = 'Relief Map of Germany.svg' } n5s7b19l0ces1totgx6a9w7xuyv5kag ಮೋಡ್ಯೂಲ್:Location map/data/Nepal 828 28035 366115 2018-06-12T12:14:11Z en>Galobtter 0 tweak 366115 Scribunto text/plain return { name = 'Nepal', top = 31.0, bottom = 26.0, left = 79.8, right = 88.5, image = 'Nepal adm location map.svg', image1 = 'Nepal rel location map.svg' } dbjc1jh7bhpftgwjaab7jbch0jbkiga 366116 366115 2026-08-21T02:53:27Z ChiK 1136 ೧ revisions imported from [[:en:Module:Location_map/data/Nepal]] 366115 Scribunto text/plain return { name = 'Nepal', top = 31.0, bottom = 26.0, left = 79.8, right = 88.5, image = 'Nepal adm location map.svg', image1 = 'Nepal rel location map.svg' } dbjc1jh7bhpftgwjaab7jbch0jbkiga ಟೆಂಪ್ಲೇಟ್:UNESCO templates 10 28036 366117 2018-08-24T08:55:39Z en>Paine Ellsworth 0 fmt 366117 wikitext text/x-wiki {{navbar|UNESCO templates|mini=1|style=float:right}} *{{tl|Infobox intangible heritage}} **{{tl|Intangible Heritage Azerbaijan}} navbox **{{tl|Intangible Heritage Iran}} navbox *{{tl|Infobox UNESCO World Heritage Site}} **{{tl|Lists of World Heritage Sites}} navbox **{{tl|World Heritage Sites in Bulgaria}} *{{tl|UNESCO Collection}} *{{tl|UNESCO Oral and Intangible Eastern Europe}} *{{tl|UNESCO Oral and Intangible music}} *{{tl|UNESCO Representative List of the Intangible Cultural Heritage of Humanity}} {{flatlist|indent=2| * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/AFR|AFR}} [Africa] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/AST|AST}} [Arab States] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/APA|APA}} [Asia and Pacific] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/ENA|ENA}} [Europe and N. America] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/LAC|LAC}} [Latin America and Caribbean] }} *{{tl|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding}} {{flatlist|indent=2| * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/AFR|AFR}} [Africa] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/AST|AST}} [Arab States] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/APA|APA}} [Asia and Pacific] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/ENA|ENA}} [Europe and N. America] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/LAC|LAC}} [Latin America and Caribbean] }}<noinclude> {{Documentation}} </noinclude> fpcf1zvwdq0tjjkfcxijecuqufgdfus 366118 366117 2026-08-21T02:53:28Z ChiK 1136 ೧ revisions imported from [[:en:Template:UNESCO_templates]] 366117 wikitext text/x-wiki {{navbar|UNESCO templates|mini=1|style=float:right}} *{{tl|Infobox intangible heritage}} **{{tl|Intangible Heritage Azerbaijan}} navbox **{{tl|Intangible Heritage Iran}} navbox *{{tl|Infobox UNESCO World Heritage Site}} **{{tl|Lists of World Heritage Sites}} navbox **{{tl|World Heritage Sites in Bulgaria}} *{{tl|UNESCO Collection}} *{{tl|UNESCO Oral and Intangible Eastern Europe}} *{{tl|UNESCO Oral and Intangible music}} *{{tl|UNESCO Representative List of the Intangible Cultural Heritage of Humanity}} {{flatlist|indent=2| * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/AFR|AFR}} [Africa] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/AST|AST}} [Arab States] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/APA|APA}} [Asia and Pacific] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/ENA|ENA}} [Europe and N. America] * {{tla|UNESCO Representative List of the Intangible Cultural Heritage of Humanity/LAC|LAC}} [Latin America and Caribbean] }} *{{tl|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding}} {{flatlist|indent=2| * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/AFR|AFR}} [Africa] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/AST|AST}} [Arab States] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/APA|APA}} [Asia and Pacific] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/ENA|ENA}} [Europe and N. America] * {{tla|UNESCO List of Intangible Cultural Heritage in Need of Urgent Safeguarding/LAC|LAC}} [Latin America and Caribbean] }}<noinclude> {{Documentation}} </noinclude> fpcf1zvwdq0tjjkfcxijecuqufgdfus ಟೆಂಪ್ಲೇಟ್:Infobox UNESCO World Heritage Site/doc 10 28037 366119 2026-07-24T13:39:14Z en>Daisy Blue 0 /* Template data */ Name set to false. Per the documentation, "leave empty, unless for stand-alone infoboxes that need a title different from the article title" 366119 wikitext text/x-wiki {{Documentation subpage}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> {{Lua|Module:Infobox|Module:InfoboxImage|Module:Check for unknown parameters}} == Syntax == <syntaxhighlight lang="wikitext" style="overflow:auto;"> {{Infobox UNESCO World Heritage Site | name = <!-- name as inscribed on the World Heritage List: use with the STAND-ALONE version of this template --> | official_name = <!-- name as inscribed on the World Heritage List: use ONLY with the embedded version of this template, if it doesn't match the name of the template it is in: see [[Giza pyramid complex]] for an example--> | image = <!-- optional --> | image_size = <!-- optional --> | caption = <!-- optional --> | part_of = <!-- optional: when the article subject is part of a WHS listing, not a full one, e.g. one church in a series of churches --> | includes = <!-- optional: when a group of subjects together form this WHS; avoid long listings, in which case it is better to summarize or to omit the parameter altogether--> | location = <!-- optional --> | criteria = <!-- Cultural, Natural or Mixed, plus roman numbers --> | ID = | link = <!-- optional: custom URL when it differs from the URL generated from ID --> | coordinates = <!-- optional; use {{coord}} --> | year = | extension = <!-- optional --> | danger = <!-- optional --> | area = <!-- optional --> | buffer_zone = <!-- optional --> | website = <!-- optional --> | pushpin_map = <!-- optional --> | child = <!-- optional; set to "yes" to embed this infobox within another infobox --> | embedded = <!-- optional; use to embed another infobox template within this one, e.g. {{Location map many}} --> }} </syntaxhighlight> ===Pre-loading Wikidata values=== The listing below proposes to import Wikidata values (called "claims" at Wikidata) and convert them to static local data, with the subst: technique. Pre-loading Wikidata values in this fashion would often be unsuccessful due to failing Wikidata claims for the item: it is up to the editor importing the claims to check whether the claim is correct and appropriate for the infobox. {{warning|several values need to be reformatted after loading with this method, see comments starting with three !!! in the list below}} The preload-list can be used for a first implementation of this template in an article. Separate parameter substitutions can be used to complement incomplete parameter sets in a previously implemented infobox. {{incomplete list}} <syntaxhighlight lang="wikitext" style="overflow:auto;"> {{Infobox UNESCO World Heritage Site | child = | name = {{subst:Wikidata|qualifier|P1435|P1448}}<!-- leave empty, unless for stand-alone infoboxes that need a title different from the article title --> | image = {{subst:Wikidata|property|P18}}<!-- !!! remove "[[File:" and "]]" wrapping the image name or it is impossible to manage the image size --> | image_upright = 1.2 | caption = {{subst:Wikidata|qualifier|P18|P2096}} | official_name = {{subst:Wikidata|qualifier|P1435|P1448}}<!-- leave empty unless the official WHS name is different from the title of the infobox --> | location = {{subst:Wikidata|properties|linked|P706}}, {{subst:Wikidata|properties|linked|P276}}, {{subst:Wikidata|properties|linked|P131}}, {{subst:Wikidata|property|linked|P17}}, {{subst:Wikidata|property|linked|P30}}<!-- make selection: P17 (country) and possibly P131 (administrative subdivision of country) suffice in most cases --> | part_of = {{subst:Wikidata|properties|linked|P361}} | includes = {{subst:Wikidata|properties|linked|P527}}<!--replace by summary if the list of sub-entities is too large or incomplete--> | criteria = {{UNESCO WHS type|{{subst:Wikidata|properties|P2614}}}}{{subst:Wikidata|properties|P2614}} | ID = {{subst:Wikidata|property|P757}} | coordinates = {{subst:Wikidata|property|linked|P625}}<!-- !!! convert to {{coord|...|...|format=dms}} --> | year = {{subst:Wikidata|qualifier|P1435|P580}} | extension = {{subst:Wikidata|qualifiers|P793|P585}} | danger = {{#if: {{subst:Wikidata|qualifiers|P793|P580}}|{{subst:Wikidata|qualifiers|P793|P580}} -|}}{{#if: {{subst:Wikidata|qualifiers|P793|P582}}|- {{subst:Wikidata|qualifiers|P793|P582}}|}} | area = {{subst:Wikidata|property|P2046}}<!-- {{convert|...|ha|acre|abbr=on}} or similar with other units recommended -->{{subst:wikidata|reference|P2046}} | buffer_zone = | website = {{URL|{{subst:Wikidata|property|P856}}}} | embedded = | pushpin_map = {{subst:Wikidata|property|P17}} | map_caption = }} </syntaxhighlight> == Notes == *If the Heritage Site has multiple locations across a country or continent, use the embedded template <nowiki>{{Location map many}}</nowiki> as in example 4. *If the template is not used as an embedded template, always use the "name" parameter. If the template is embedded, ''and'' the official name at the Unesco site doesn't match the name of the template it is used in, use "official_name". See [[Giza pyramid complex]] for an example of the second use. *"Type" is kept as a parameter for backwards compatibility only, and should preferably not be used. == Pushpin map == {{Location map/doc/infobox parameters}} ==Examples== ;Example 1: {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} This example <!--uses the default infobox width of 20[[Em (typography)|em]] and--> has no image: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} </syntaxhighlight> {{clear}} ;Example 2: {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | image_size = 270px | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} This example uses <!--the default infobox width of 20[[Em (typography)|em]] with--> an image of width 270px, and the alt and caption parameters: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | image_size = 270px | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} </syntaxhighlight> {{clear}} ;Example 3: {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 | area = 167.37 ha | buffer_zone = 70.29 ha | pushpin_map = Nepal | coordinates = {{coord|27.704|85.309}} | map_caption = Location in Nepal }} This example uses the default image width, and a location map: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 | area = 167.37 ha | buffer_zone = 70.29 ha | pushpin_map = Nepal | coordinates = {{coord|27.704|85.309}} | map_caption = Location in Nepal }} </syntaxhighlight> {{clear}} ;Example 4: {{Infobox UNESCO World Heritage Site |name = Bauhaus and its Sites in Weimar, Dessau and Bernau |image = [[File:Bundesarchiv Bild 183-1987-0204-305, Dessau, Bauhaus.jpg|250px|Bauhaus Dessau Workshop]] |caption = Bauhaus building in Dessau |location = Germany |criteria = Cultural: ii, iv, vi |ID = 729 |year = 1996 |extension = 2017 |area = 8.1614 ha |buffer_zone = 59.26 ha |embedded = {{Location map many|border = infobox | Germany | caption = Bauhaus sites: 1 = Weimar, 2 = Dessau, 3 = Bernau | label1 = 1 | link1 = Weimar | coordinates1 = {{coord|50|58|29|N|11|19|46|E}} | label2 = 2 | link2 = Dessau | coordinates2 = {{coord|51|50|18|N|12|13|38|E}} | label3 = 3 | link3 = Bernau | coordinates3 = {{coord|52|42|24|N|13|32|39|E}} }} }} This example uses an embedded template for the location map, allowing an unlimited number of clickable locations: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site |name = Bauhaus and its Sites in Weimar, Dessau and Bernau |image = [[File:Bundesarchiv Bild 183-1987-0204-305, Dessau, Bauhaus.jpg|250px|Bauhaus Dessau Workshop]] |caption = Bauhaus building in Dessau |location = Germany |criteria = Cultural: ii, iv, vi |ID = 729 |year = 1996 |extension = 2017 |area = 8.1614 ha |buffer_zone = 59.26 ha |embedded = {{Location map many|border = infobox | Germany | caption = Bauhaus sites: 1 = Weimar, 2 = Dessau, 3 = Bernau | label1 = 1 | link1 = Weimar | coordinates1 = {{coord|50|58|29|N|11|19|46|E}} | label2 = 2 | link2 = Dessau | coordinates2 = {{coord|51|50|18|N|12|13|38|E}} | label3 = 3 | link3 = Bernau bei Berlin | coordinates3 = {{coord|52|42|24|N|13|32|39|E}} }} }} </syntaxhighlight> {{clear}} == Template data == {{collapse top|title=[[Wikipedia:TemplateData|TemplateData]] documentation used by [[Wikipedia:VisualEditor|VisualEditor]] and other tools}} {{TemplateData header|noheader=1}} <templatedata> { "description": "An Infobox for World Heritage Sites", "params": { "name": { "label": "Name", "description": "name as inscribed on the World Heritage List", "type": "string", "required": false }, "image": { "label": "Image", "description": "An image of the site", "type": "string", "required": false }, "caption": { "label": "Caption", "description": "The image caption", "type": "string", "required": false }, "location": { "label": "Location", "description": "Location of the site", "type": "string", "required": false }, "criteria": { "label": "Criteria", "description": "The designated criteria i, ... ix or x", "type": "string", "required": true }, "ID": { "label": "ID", "description": "The reference number", "type": "string", "required": true }, "link": { "label": "Link", "description": "Custom URL for the reference link; requires ID and overrides the URL automatically generated from it", "type": "url", "required": false }, "coordinates": { "label": "Coordinates", "description": "Use the {{coord}} template with display=inline, do not be overly precise.", "type": "string", "required": false, "aliases": ["coordinates"] }, "year": { "label": "Year", "description": "Year site was listed", "type": "string", "required": false }, "session": { "label": "Session", "description": "Session site was inscribed: 1st – 40th", "type": "string", "required": false }, "extension": { "label": "Extension", "description": "Extension", "type": "string", "required": false }, "danger": { "label": "Danger", "description": "Period site has been in danger", "type": "string", "required": false }, "pushpin_map": { "label": "Location map", "description": "Name of Location map to use, typically a country name", "type": "string", "required": false }, "child": { "label": "Child", "description": "set to 'yes' to embed this infobox within another infobox", "type": "string", "required": false }, "embedded": { "label": "Embedded", "description": "Use to embed another infobox template within this one, e.g. {{Infobox mountain}}", "type": "string", "required": false }, "hide_interactive_map": { "label": "Hide map", "description": "Use to hide the default interactive map which only shows the location of one site", "type": "string", "required": false } } } </templatedata> {{collapse bottom}} == Tracking categories == {{infobox tracking categories }} == See also == {{UNESCO templates}} <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:World Heritage Site templates| ]] [[Category:Place infobox templates|World Heritage Site]] [[Category:Infobox templates with module parameter|World Heritage Site]] [[Category:Embeddable templates|{{PAGENAME}}]] [[Category:Templates that add a tracking category]] [[Category:WikiProject Protected areas|τ]] [[Category:WikiProject World Heritage Sites|τ]] [[Category:Templates with coordinates fields]] }}</includeonly> mzzmgpqzy725ahj3if958t38w0oeivv 366120 366119 2026-08-21T02:53:28Z ChiK 1136 ೧ revisions imported from [[:en:Template:Infobox_UNESCO_World_Heritage_Site/doc]] 366119 wikitext text/x-wiki {{Documentation subpage}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> {{Lua|Module:Infobox|Module:InfoboxImage|Module:Check for unknown parameters}} == Syntax == <syntaxhighlight lang="wikitext" style="overflow:auto;"> {{Infobox UNESCO World Heritage Site | name = <!-- name as inscribed on the World Heritage List: use with the STAND-ALONE version of this template --> | official_name = <!-- name as inscribed on the World Heritage List: use ONLY with the embedded version of this template, if it doesn't match the name of the template it is in: see [[Giza pyramid complex]] for an example--> | image = <!-- optional --> | image_size = <!-- optional --> | caption = <!-- optional --> | part_of = <!-- optional: when the article subject is part of a WHS listing, not a full one, e.g. one church in a series of churches --> | includes = <!-- optional: when a group of subjects together form this WHS; avoid long listings, in which case it is better to summarize or to omit the parameter altogether--> | location = <!-- optional --> | criteria = <!-- Cultural, Natural or Mixed, plus roman numbers --> | ID = | link = <!-- optional: custom URL when it differs from the URL generated from ID --> | coordinates = <!-- optional; use {{coord}} --> | year = | extension = <!-- optional --> | danger = <!-- optional --> | area = <!-- optional --> | buffer_zone = <!-- optional --> | website = <!-- optional --> | pushpin_map = <!-- optional --> | child = <!-- optional; set to "yes" to embed this infobox within another infobox --> | embedded = <!-- optional; use to embed another infobox template within this one, e.g. {{Location map many}} --> }} </syntaxhighlight> ===Pre-loading Wikidata values=== The listing below proposes to import Wikidata values (called "claims" at Wikidata) and convert them to static local data, with the subst: technique. Pre-loading Wikidata values in this fashion would often be unsuccessful due to failing Wikidata claims for the item: it is up to the editor importing the claims to check whether the claim is correct and appropriate for the infobox. {{warning|several values need to be reformatted after loading with this method, see comments starting with three !!! in the list below}} The preload-list can be used for a first implementation of this template in an article. Separate parameter substitutions can be used to complement incomplete parameter sets in a previously implemented infobox. {{incomplete list}} <syntaxhighlight lang="wikitext" style="overflow:auto;"> {{Infobox UNESCO World Heritage Site | child = | name = {{subst:Wikidata|qualifier|P1435|P1448}}<!-- leave empty, unless for stand-alone infoboxes that need a title different from the article title --> | image = {{subst:Wikidata|property|P18}}<!-- !!! remove "[[File:" and "]]" wrapping the image name or it is impossible to manage the image size --> | image_upright = 1.2 | caption = {{subst:Wikidata|qualifier|P18|P2096}} | official_name = {{subst:Wikidata|qualifier|P1435|P1448}}<!-- leave empty unless the official WHS name is different from the title of the infobox --> | location = {{subst:Wikidata|properties|linked|P706}}, {{subst:Wikidata|properties|linked|P276}}, {{subst:Wikidata|properties|linked|P131}}, {{subst:Wikidata|property|linked|P17}}, {{subst:Wikidata|property|linked|P30}}<!-- make selection: P17 (country) and possibly P131 (administrative subdivision of country) suffice in most cases --> | part_of = {{subst:Wikidata|properties|linked|P361}} | includes = {{subst:Wikidata|properties|linked|P527}}<!--replace by summary if the list of sub-entities is too large or incomplete--> | criteria = {{UNESCO WHS type|{{subst:Wikidata|properties|P2614}}}}{{subst:Wikidata|properties|P2614}} | ID = {{subst:Wikidata|property|P757}} | coordinates = {{subst:Wikidata|property|linked|P625}}<!-- !!! convert to {{coord|...|...|format=dms}} --> | year = {{subst:Wikidata|qualifier|P1435|P580}} | extension = {{subst:Wikidata|qualifiers|P793|P585}} | danger = {{#if: {{subst:Wikidata|qualifiers|P793|P580}}|{{subst:Wikidata|qualifiers|P793|P580}} -|}}{{#if: {{subst:Wikidata|qualifiers|P793|P582}}|- {{subst:Wikidata|qualifiers|P793|P582}}|}} | area = {{subst:Wikidata|property|P2046}}<!-- {{convert|...|ha|acre|abbr=on}} or similar with other units recommended -->{{subst:wikidata|reference|P2046}} | buffer_zone = | website = {{URL|{{subst:Wikidata|property|P856}}}} | embedded = | pushpin_map = {{subst:Wikidata|property|P17}} | map_caption = }} </syntaxhighlight> == Notes == *If the Heritage Site has multiple locations across a country or continent, use the embedded template <nowiki>{{Location map many}}</nowiki> as in example 4. *If the template is not used as an embedded template, always use the "name" parameter. If the template is embedded, ''and'' the official name at the Unesco site doesn't match the name of the template it is used in, use "official_name". See [[Giza pyramid complex]] for an example of the second use. *"Type" is kept as a parameter for backwards compatibility only, and should preferably not be used. == Pushpin map == {{Location map/doc/infobox parameters}} ==Examples== ;Example 1: {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} This example <!--uses the default infobox width of 20[[Em (typography)|em]] and--> has no image: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} </syntaxhighlight> {{clear}} ;Example 2: {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | image_size = 270px | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} This example uses <!--the default infobox width of 20[[Em (typography)|em]] with--> an image of width 270px, and the alt and caption parameters: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | image_size = 270px | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 }} </syntaxhighlight> {{clear}} ;Example 3: {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 | area = 167.37 ha | buffer_zone = 70.29 ha | pushpin_map = Nepal | coordinates = {{coord|27.704|85.309}} | map_caption = Location in Nepal }} This example uses the default image width, and a location map: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site | name = Kathmandu Valley | image = Basantapurpalace.JPG | alt = The Kathmandu Darbar Square | caption = The Kathmandu Darbar Square | location = [[Nepal]] | criteria = Cultural: iii, iv, vi | ID = 121 | year = 1979 | danger = 2003-2007 | extension = 2006 | area = 167.37 ha | buffer_zone = 70.29 ha | pushpin_map = Nepal | coordinates = {{coord|27.704|85.309}} | map_caption = Location in Nepal }} </syntaxhighlight> {{clear}} ;Example 4: {{Infobox UNESCO World Heritage Site |name = Bauhaus and its Sites in Weimar, Dessau and Bernau |image = [[File:Bundesarchiv Bild 183-1987-0204-305, Dessau, Bauhaus.jpg|250px|Bauhaus Dessau Workshop]] |caption = Bauhaus building in Dessau |location = Germany |criteria = Cultural: ii, iv, vi |ID = 729 |year = 1996 |extension = 2017 |area = 8.1614 ha |buffer_zone = 59.26 ha |embedded = {{Location map many|border = infobox | Germany | caption = Bauhaus sites: 1 = Weimar, 2 = Dessau, 3 = Bernau | label1 = 1 | link1 = Weimar | coordinates1 = {{coord|50|58|29|N|11|19|46|E}} | label2 = 2 | link2 = Dessau | coordinates2 = {{coord|51|50|18|N|12|13|38|E}} | label3 = 3 | link3 = Bernau | coordinates3 = {{coord|52|42|24|N|13|32|39|E}} }} }} This example uses an embedded template for the location map, allowing an unlimited number of clickable locations: <syntaxhighlight lang="wikitext" style="width:55%; overflow:auto; margin-right:5px; font-size:95%;"> {{Infobox UNESCO World Heritage Site |name = Bauhaus and its Sites in Weimar, Dessau and Bernau |image = [[File:Bundesarchiv Bild 183-1987-0204-305, Dessau, Bauhaus.jpg|250px|Bauhaus Dessau Workshop]] |caption = Bauhaus building in Dessau |location = Germany |criteria = Cultural: ii, iv, vi |ID = 729 |year = 1996 |extension = 2017 |area = 8.1614 ha |buffer_zone = 59.26 ha |embedded = {{Location map many|border = infobox | Germany | caption = Bauhaus sites: 1 = Weimar, 2 = Dessau, 3 = Bernau | label1 = 1 | link1 = Weimar | coordinates1 = {{coord|50|58|29|N|11|19|46|E}} | label2 = 2 | link2 = Dessau | coordinates2 = {{coord|51|50|18|N|12|13|38|E}} | label3 = 3 | link3 = Bernau bei Berlin | coordinates3 = {{coord|52|42|24|N|13|32|39|E}} }} }} </syntaxhighlight> {{clear}} == Template data == {{collapse top|title=[[Wikipedia:TemplateData|TemplateData]] documentation used by [[Wikipedia:VisualEditor|VisualEditor]] and other tools}} {{TemplateData header|noheader=1}} <templatedata> { "description": "An Infobox for World Heritage Sites", "params": { "name": { "label": "Name", "description": "name as inscribed on the World Heritage List", "type": "string", "required": false }, "image": { "label": "Image", "description": "An image of the site", "type": "string", "required": false }, "caption": { "label": "Caption", "description": "The image caption", "type": "string", "required": false }, "location": { "label": "Location", "description": "Location of the site", "type": "string", "required": false }, "criteria": { "label": "Criteria", "description": "The designated criteria i, ... ix or x", "type": "string", "required": true }, "ID": { "label": "ID", "description": "The reference number", "type": "string", "required": true }, "link": { "label": "Link", "description": "Custom URL for the reference link; requires ID and overrides the URL automatically generated from it", "type": "url", "required": false }, "coordinates": { "label": "Coordinates", "description": "Use the {{coord}} template with display=inline, do not be overly precise.", "type": "string", "required": false, "aliases": ["coordinates"] }, "year": { "label": "Year", "description": "Year site was listed", "type": "string", "required": false }, "session": { "label": "Session", "description": "Session site was inscribed: 1st – 40th", "type": "string", "required": false }, "extension": { "label": "Extension", "description": "Extension", "type": "string", "required": false }, "danger": { "label": "Danger", "description": "Period site has been in danger", "type": "string", "required": false }, "pushpin_map": { "label": "Location map", "description": "Name of Location map to use, typically a country name", "type": "string", "required": false }, "child": { "label": "Child", "description": "set to 'yes' to embed this infobox within another infobox", "type": "string", "required": false }, "embedded": { "label": "Embedded", "description": "Use to embed another infobox template within this one, e.g. {{Infobox mountain}}", "type": "string", "required": false }, "hide_interactive_map": { "label": "Hide map", "description": "Use to hide the default interactive map which only shows the location of one site", "type": "string", "required": false } } } </templatedata> {{collapse bottom}} == Tracking categories == {{infobox tracking categories }} == See also == {{UNESCO templates}} <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:World Heritage Site templates| ]] [[Category:Place infobox templates|World Heritage Site]] [[Category:Infobox templates with module parameter|World Heritage Site]] [[Category:Embeddable templates|{{PAGENAME}}]] [[Category:Templates that add a tracking category]] [[Category:WikiProject Protected areas|τ]] [[Category:WikiProject World Heritage Sites|τ]] [[Category:Templates with coordinates fields]] }}</includeonly> mzzmgpqzy725ahj3if958t38w0oeivv ಬಾತುರಿನ್ 0 28038 366122 2026-08-21T03:12:44Z Hariprasad Shetty10 6127 ಹೊಸ ಪುಟ: {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ... 366122 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == 3ea7mstjarb5re65gp6p2rsxeihfhb7 366123 366122 2026-08-21T03:12:57Z Hariprasad Shetty10 6127 /* ಇತಿಹಾಸ */ 366123 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> rxirrop6eelld8clogck7ffbe95062q 366124 366123 2026-08-21T03:13:12Z Hariprasad Shetty10 6127 /* ಸುರುತ ಇತಿಹಾಸ */ 366124 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> qw3jep471pzb42t7qubc95hw5c9xz32 366125 366124 2026-08-21T03:13:37Z Hariprasad Shetty10 6127 /* ಕೋಸಾಕ್ ಕಾಲೊ */ 366125 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ === 1669ಡ್ ಹೆಟ್‌ಮಾನ್ ಡೆಮಿಯನ್ ಮ್ನೊಹೊಹ್ರಿಶ್ನಿ ಬಾತುರಿನ್‌ನು ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಉಂದು ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಬೆಳೆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ (1687–1708) ಕಾಲೊಡು ಬಾತುರಿನ್ ಮಸ್ತ್ ಅಭಿವೃದ್ಧಿ ಆಂಡ್. ಪಟ್ಟಣದ ಜನಸಂಖ್ಯೆ ಜಾಸ್ತಿ ಆಯಿನೆರ್ ಬೊಕ್ಕ ಇತ್ತೆ ಮಸ್ತ್ ಚರ್ಚುಲು, ಚಾಪೆಲ್‌ಲು ಬೊಕ್ಕ ರಡ್ಡ್ ಮಠೊಲು ಇತ್ತುಂಡು. ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಜನರಲ್ ಮಿಲಿಟರಿ ಚಾನ್ಸೆಲರಿ ಬೊಕ್ಕ ಜನರಲ್ ಮಿಲಿಟರಿ ಕೋರ್ಟ್ ಕೂಡ ಬಾತುರಿನ್‌ಡ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಝೆಪಾ ಬಾತುರಿನ್ ಕೈತಲ್ದ ಹೊಂಚಾರಿವ್ಕಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಬಲಿಷ್ಠ ಅರಮನೆ ನಿರ್ಮಿಸಾಯೆರ್. ಉಂದು ಅರೆನ ಆಡಳಿತೊದ ಪ್ರಮುಖ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> k206cbglxd96s1m0t9i56tnc8x10pdi 366126 366125 2026-08-21T03:13:53Z Hariprasad Shetty10 6127 /* ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ */ 366126 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ === 1669ಡ್ ಹೆಟ್‌ಮಾನ್ ಡೆಮಿಯನ್ ಮ್ನೊಹೊಹ್ರಿಶ್ನಿ ಬಾತುರಿನ್‌ನು ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಉಂದು ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಬೆಳೆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ (1687–1708) ಕಾಲೊಡು ಬಾತುರಿನ್ ಮಸ್ತ್ ಅಭಿವೃದ್ಧಿ ಆಂಡ್. ಪಟ್ಟಣದ ಜನಸಂಖ್ಯೆ ಜಾಸ್ತಿ ಆಯಿನೆರ್ ಬೊಕ್ಕ ಇತ್ತೆ ಮಸ್ತ್ ಚರ್ಚುಲು, ಚಾಪೆಲ್‌ಲು ಬೊಕ್ಕ ರಡ್ಡ್ ಮಠೊಲು ಇತ್ತುಂಡು. ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಜನರಲ್ ಮಿಲಿಟರಿ ಚಾನ್ಸೆಲರಿ ಬೊಕ್ಕ ಜನರಲ್ ಮಿಲಿಟರಿ ಕೋರ್ಟ್ ಕೂಡ ಬಾತುರಿನ್‌ಡ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಝೆಪಾ ಬಾತುರಿನ್ ಕೈತಲ್ದ ಹೊಂಚಾರಿವ್ಕಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಬಲಿಷ್ಠ ಅರಮನೆ ನಿರ್ಮಿಸಾಯೆರ್. ಉಂದು ಅರೆನ ಆಡಳಿತೊದ ಪ್ರಮುಖ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಬಾತುರಿನ್ ದುರಂತ === 1708ಡ್ ಮಹಾ ಉತ್ತರ ಯುದ್ಧದ ಸಮಯೊಡು ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ ರಷ್ಯಾದ ಝಾರ್ ಪೀಟರ್ ದಿ ಗ್ರೇಟ್ಗ್ ವಿರೋಧವಾದ್ ಸ್ವೀಡನ್‌ದ ರಾಜೆ ಚಾರ್ಲ್ಸ್ XIIದ ಒಟ್ಟುಗ್ ಸೇರಾಯೆರ್. ಅಯಿನ ಬುಕ್ಕೊ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ಮುತ್ತಿಗೆ ಪಾಡಿಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1708ರ ನವೆಂಬರ್‌ಡ್ ಅಲೆಕ್ಸಾಂಡರ್ ಮೆನ್ಶಿಕೊವ್ ನೇತೃತ್ವದ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೊಂದು ಪಟ್ಟಣೊನು ನಾಶ ಮಲ್ತೆರ್. ಈ ಘಟನೆನು ಉಕ್ರೇನಿಯನ್ ಇತಿಹಾಸೊಡು '''ಬಾತುರಿನ್ ದುರಂತ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಈ ದಾಳಿಡ್ ಪಟ್ಟಣೊದ್ ಮಸ್ತ್ ಜನಕುಲು ಬೊಕ್ಕ ರಕ್ಷಣಾ ಪಡೆಲು ತೀರುದು ಪೋಯೆರ್. ಬಾತುರಿನ್ ಸಂಪೂರ್ಣವಾದ್ ನಾಶ ಆಯಿನ ಬುಕ್ಕೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿನ [[ಗ್ಲುಖಿವ್]]ಗು ಬದಲಾಯಿಸಾಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 6pwxl6i2m5lk567olp7jkjtmbdce5mx 366127 366126 2026-08-21T03:14:06Z Hariprasad Shetty10 6127 /* ಬಾತುರಿನ್ ದುರಂತ */ 366127 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ === 1669ಡ್ ಹೆಟ್‌ಮಾನ್ ಡೆಮಿಯನ್ ಮ್ನೊಹೊಹ್ರಿಶ್ನಿ ಬಾತುರಿನ್‌ನು ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಉಂದು ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಬೆಳೆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ (1687–1708) ಕಾಲೊಡು ಬಾತುರಿನ್ ಮಸ್ತ್ ಅಭಿವೃದ್ಧಿ ಆಂಡ್. ಪಟ್ಟಣದ ಜನಸಂಖ್ಯೆ ಜಾಸ್ತಿ ಆಯಿನೆರ್ ಬೊಕ್ಕ ಇತ್ತೆ ಮಸ್ತ್ ಚರ್ಚುಲು, ಚಾಪೆಲ್‌ಲು ಬೊಕ್ಕ ರಡ್ಡ್ ಮಠೊಲು ಇತ್ತುಂಡು. ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಜನರಲ್ ಮಿಲಿಟರಿ ಚಾನ್ಸೆಲರಿ ಬೊಕ್ಕ ಜನರಲ್ ಮಿಲಿಟರಿ ಕೋರ್ಟ್ ಕೂಡ ಬಾತುರಿನ್‌ಡ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಝೆಪಾ ಬಾತುರಿನ್ ಕೈತಲ್ದ ಹೊಂಚಾರಿವ್ಕಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಬಲಿಷ್ಠ ಅರಮನೆ ನಿರ್ಮಿಸಾಯೆರ್. ಉಂದು ಅರೆನ ಆಡಳಿತೊದ ಪ್ರಮುಖ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಬಾತುರಿನ್ ದುರಂತ === 1708ಡ್ ಮಹಾ ಉತ್ತರ ಯುದ್ಧದ ಸಮಯೊಡು ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ ರಷ್ಯಾದ ಝಾರ್ ಪೀಟರ್ ದಿ ಗ್ರೇಟ್ಗ್ ವಿರೋಧವಾದ್ ಸ್ವೀಡನ್‌ದ ರಾಜೆ ಚಾರ್ಲ್ಸ್ XIIದ ಒಟ್ಟುಗ್ ಸೇರಾಯೆರ್. ಅಯಿನ ಬುಕ್ಕೊ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ಮುತ್ತಿಗೆ ಪಾಡಿಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1708ರ ನವೆಂಬರ್‌ಡ್ ಅಲೆಕ್ಸಾಂಡರ್ ಮೆನ್ಶಿಕೊವ್ ನೇತೃತ್ವದ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೊಂದು ಪಟ್ಟಣೊನು ನಾಶ ಮಲ್ತೆರ್. ಈ ಘಟನೆನು ಉಕ್ರೇನಿಯನ್ ಇತಿಹಾಸೊಡು '''ಬಾತುರಿನ್ ದುರಂತ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಈ ದಾಳಿಡ್ ಪಟ್ಟಣೊದ್ ಮಸ್ತ್ ಜನಕುಲು ಬೊಕ್ಕ ರಕ್ಷಣಾ ಪಡೆಲು ತೀರುದು ಪೋಯೆರ್. ಬಾತುರಿನ್ ಸಂಪೂರ್ಣವಾದ್ ನಾಶ ಆಯಿನ ಬುಕ್ಕೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿನ [[ಗ್ಲುಖಿವ್]]ಗು ಬದಲಾಯಿಸಾಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಪುನರ್‌ನಿರ್ಮಾಣ ಬೊಕ್ಕ 18ನೇ ಶತಮಾನೊ === ಬಾತುರಿನ್ 1750ರ ದಶಕೊಡು ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್. 1750ಡ್ದ್ 1764ರ ತನಕ ಹೆಟ್‌ಮಾನ್ ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಬಾತುರಿನ್‌ನು ಕುಡ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ರಝುಮೊವ್ಸ್ಕಿದ್ ಕಾಲೊಡು ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಭವ್ಯ ಅರಮನೆ ನಿರ್ಮಾಣ ಆಂಡ್. ಈ ಅರಮನೆನು ವಾಸ್ತುಶಿಲ್ಪಿ ಆಂಡ್ರೆ ಕ್ವಾಸೊವ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕ ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ಅರೆನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1764ಡ್ ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಸ್ವಾಯತ್ತತೆನು ರದ್ದ್ ಮಲ್ತಿನ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ರಾಜಕೀಯ ಮಹತ್ವೊ ಕಡಿಮೆ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> h6my9fy0sjthpkybr6c7usyo71ddg9h 366128 366127 2026-08-21T03:14:22Z Hariprasad Shetty10 6127 /* ಪುನರ್‌ನಿರ್ಮಾಣ ಬೊಕ್ಕ 18ನೇ ಶತಮಾನೊ */ 366128 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ === 1669ಡ್ ಹೆಟ್‌ಮಾನ್ ಡೆಮಿಯನ್ ಮ್ನೊಹೊಹ್ರಿಶ್ನಿ ಬಾತುರಿನ್‌ನು ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಉಂದು ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಬೆಳೆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ (1687–1708) ಕಾಲೊಡು ಬಾತುರಿನ್ ಮಸ್ತ್ ಅಭಿವೃದ್ಧಿ ಆಂಡ್. ಪಟ್ಟಣದ ಜನಸಂಖ್ಯೆ ಜಾಸ್ತಿ ಆಯಿನೆರ್ ಬೊಕ್ಕ ಇತ್ತೆ ಮಸ್ತ್ ಚರ್ಚುಲು, ಚಾಪೆಲ್‌ಲು ಬೊಕ್ಕ ರಡ್ಡ್ ಮಠೊಲು ಇತ್ತುಂಡು. ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಜನರಲ್ ಮಿಲಿಟರಿ ಚಾನ್ಸೆಲರಿ ಬೊಕ್ಕ ಜನರಲ್ ಮಿಲಿಟರಿ ಕೋರ್ಟ್ ಕೂಡ ಬಾತುರಿನ್‌ಡ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಝೆಪಾ ಬಾತುರಿನ್ ಕೈತಲ್ದ ಹೊಂಚಾರಿವ್ಕಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಬಲಿಷ್ಠ ಅರಮನೆ ನಿರ್ಮಿಸಾಯೆರ್. ಉಂದು ಅರೆನ ಆಡಳಿತೊದ ಪ್ರಮುಖ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಬಾತುರಿನ್ ದುರಂತ === 1708ಡ್ ಮಹಾ ಉತ್ತರ ಯುದ್ಧದ ಸಮಯೊಡು ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ ರಷ್ಯಾದ ಝಾರ್ ಪೀಟರ್ ದಿ ಗ್ರೇಟ್ಗ್ ವಿರೋಧವಾದ್ ಸ್ವೀಡನ್‌ದ ರಾಜೆ ಚಾರ್ಲ್ಸ್ XIIದ ಒಟ್ಟುಗ್ ಸೇರಾಯೆರ್. ಅಯಿನ ಬುಕ್ಕೊ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ಮುತ್ತಿಗೆ ಪಾಡಿಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1708ರ ನವೆಂಬರ್‌ಡ್ ಅಲೆಕ್ಸಾಂಡರ್ ಮೆನ್ಶಿಕೊವ್ ನೇತೃತ್ವದ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೊಂದು ಪಟ್ಟಣೊನು ನಾಶ ಮಲ್ತೆರ್. ಈ ಘಟನೆನು ಉಕ್ರೇನಿಯನ್ ಇತಿಹಾಸೊಡು '''ಬಾತುರಿನ್ ದುರಂತ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಈ ದಾಳಿಡ್ ಪಟ್ಟಣೊದ್ ಮಸ್ತ್ ಜನಕುಲು ಬೊಕ್ಕ ರಕ್ಷಣಾ ಪಡೆಲು ತೀರುದು ಪೋಯೆರ್. ಬಾತುರಿನ್ ಸಂಪೂರ್ಣವಾದ್ ನಾಶ ಆಯಿನ ಬುಕ್ಕೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿನ [[ಗ್ಲುಖಿವ್]]ಗು ಬದಲಾಯಿಸಾಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಪುನರ್‌ನಿರ್ಮಾಣ ಬೊಕ್ಕ 18ನೇ ಶತಮಾನೊ === ಬಾತುರಿನ್ 1750ರ ದಶಕೊಡು ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್. 1750ಡ್ದ್ 1764ರ ತನಕ ಹೆಟ್‌ಮಾನ್ ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಬಾತುರಿನ್‌ನು ಕುಡ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ರಝುಮೊವ್ಸ್ಕಿದ್ ಕಾಲೊಡು ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಭವ್ಯ ಅರಮನೆ ನಿರ್ಮಾಣ ಆಂಡ್. ಈ ಅರಮನೆನು ವಾಸ್ತುಶಿಲ್ಪಿ ಆಂಡ್ರೆ ಕ್ವಾಸೊವ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕ ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ಅರೆನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1764ಡ್ ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಸ್ವಾಯತ್ತತೆನು ರದ್ದ್ ಮಲ್ತಿನ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ರಾಜಕೀಯ ಮಹತ್ವೊ ಕಡಿಮೆ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಪರಂಪರೆ == ಬಾತುರಿನ್‌ಡ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಕಾಲೊದ ಮಸ್ತ್ ಐತಿಹಾಸಿಕ ಸ್ಮಾರಕೊಲು ಇನಿ ಒರಿದುಂಡು. ಅವುಲೆಡ್ ಮುಖ್ಯವಾದ್ದ್ '''ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ''', '''ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್''', '''ವಾಸಿಲ್ ಕೊಚುಬೈದ ಇಲ್ಲ್''' ಬೊಕ್ಕ ಮಸ್ತ್ ಚರ್ಚುಲೆನ ಅವಶೇಷೊಲುಲಾ ಉಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ ಬಾತುರಿನ್‌ದ ಪ್ರಮುಖ ವಾಸ್ತುಶಿಲ್ಪದ ಸ್ಮಾರಕೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಇಜಿ 18ನೇ ಶತಮಾನೊದ್ ನಿರ್ಮಾಣ ಆಯಿನೆರ್ ಬೊಕ್ಕ ನಂತರೊಡು ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://en.wikipedia.org/wiki/Baturyn |access-date=20 August 2026 }}</ref> o5o901uwhowa881zi8njexf7d6wo4fd 366129 366128 2026-08-21T03:14:39Z Hariprasad Shetty10 6127 /* ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಪರಂಪರೆ */ 366129 wikitext text/x-wiki {{Infobox settlement | official_name = ಬಾತುರಿನ್ | native_name = | native_name_lang = uk | settlement_type = ಪಟ್ಟಣ | subdivision_type = ದೇಶ | subdivision_name = ಉಕ್ರೇನ್ | subdivision_type1 = ಒಬ್ಲಾಸ್ಟ್ | subdivision_name1 = ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ | subdivision_type2 = ರೈಯಾನ್ | subdivision_name2 = ನಿಝಿನ್ ರೈಯಾನ್ | subdivision_type3 = ಹ್ರೊಮಾಡಾ | subdivision_name3 = ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾ | image_skyline = Батуринська фортеця-11.jpg | image_caption = ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್ | image_seal = Герб Батурин.svg | image_flag = Baturyn flag.png | population_total = 2,406 | population_as_of = 2022 | area_total_km2 = 7 | established_date = 1625 | coordinates = {{Coord|51|21|N|32|53|E|region:UA|display=inline,title}} | timezone = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಸಮಯ | utc_offset = +2 | timezone_DST = ಪೂರ್ವ ಯುರೋಪಿಯನ್ ಬೇಸಿಗೆ ಸಮಯ | utc_offset_DST = +3 | postal_code_type = ಅಂಚೆ ಸಂಕೇತ | postal_code = 16512 | area_code_type = ದೂರವಾಣಿ ಕೋಡ್ | area_code = +380 4635 | mapframe = yes | mapframe-zoom = 12 }} '''ಬಾತುರಿನ್''' ಉತ್ತರ ಉಕ್ರೇನ್ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡ್, ನಿಝಿನ್ ರೈಯಾನ್ಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಪಟ್ಟಣ ಆದುಂಡು. ಉಂದು ಸೆಯ್ಮ್ ತುದೆತ ದಂಡೆಡ್ ಉಂಡು. ಬಾತುರಿನ್ ಇತ್ತೆ ಬಾತುರಿನ್ ನಗರ ಹ್ರೊಮಾಡಾದ ಆಡಳಿತ ಕೇಂದ್ರೊ ಆದುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ವಿಶೇಷವಾದ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ದ ರಾಜಧಾನಿ ಆದ್ ಪ್ರಸಿದ್ಧ ಆಯಿನ ಪಟ್ಟಣ. 1669ಡ್ದ್ 1708ರ ತನಕ ಬೊಕ್ಕ 1750ಡ್ದ್ 1764ರ ತನಕ ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಸುರುತ ಇತಿಹಾಸ === ಬಾತುರಿನ್ ಇಪ್ಪುನ ಪ್ರದೇಶೊಡು 9ನೇ–10ನೇ ಶತಮಾನೊದ್ ಮಾನವ ನೆಲೆ ಇತ್ತ್ಂಡ್. 11ನೇಡ್ದ್ 13ನೇ ಶತಮಾನೊದ್ ಈ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಕೋಟೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ನೆಲೆ ಇತ್ತ್ಂಡ್. ಮಂಗೋಲ್ ದಾಳಿದ ಕಾಲೊಡು ಈ ಕೋಟೆದ ನೆಲೆ ನಾಶ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಬಾತುರಿನ್ ಪುದರ್ ಸುರುತ ಲಿಖಿತ ದಾಖಲೆಲೆಡ್ 1620ರ ದಶಕೊಡು ತೋಜುದು ಬೈದುಂಡು. ಆ ಕಾಲೊಡು ಚೆರ್ನಿಹಿವ್ ಪ್ರದೇಶೊ ಪೋಲಿಷ್–ಲಿಥುವೇನಿಯನ್ ಕಾಮನ್ವೆಲ್ತ್ ದ ಭಾಗೊ ಆದ್ ಇತ್ತುಂಡು. 1625ಡ್ ಪೋಲಿಷ್ ಸರ್ಕಾರದ ಅನುಮತಿಡ್ ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಕೋಟೆ ನಿರ್ಮಾಣ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1633ಡ್ ಸ್ಮೋಲೆನ್ಸ್ಕ್ ಯುದ್ಧೊದ ಸಮಯೊಡು ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್ ಕೋಟೆನು ವಶೊಗು ದೆತ್ತ್ ಅವೆನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಕಾಲೊ === 1648–1657ರ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ ದಂಗೆದ ಸಮಯೊಡು ಕೋಸಾಕ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೆರ್. 1648ಡ್ ಇಡೀ ಕೋಸಾಕ್ ಆಡಳಿತೊದ ಒಂಜಿ ಪ್ರಾದೇಶಿಕ ಕೇಂದ್ರೊ ಆಂಡ್ರ್.ಸುರುಟು ಉಂದು ಸ್ಟಾರೊಡುಬ್ ಕೋಸಾಕ್ ರೆಜಿಮೆಂಟ್‌ದ್ ಬೊಕ್ಕ ನಿಝಿನ್ ರೆಜಿಮೆಂಟ್‌ಗ್ ಸೇರುದು ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1654ಡ್ ಪೆರೆಯಾಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ್ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ಜನಕುಲು ರಷ್ಯನ್ ಝಾರ್‌ಗ್ ನಿಷ್ಠೆದ ಪ್ರಮಾಣ ಮಂತಿನೊ ಬುಕ್ಕೊ ಪಟ್ಟಣೊನ್ ಪೊತ್ತಾದ್ ನಾಶ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1663ಡ್ ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಬ್ರಿಯುಖೊವೆಟ್ಸ್ಕಿ ಬಾತುರಿನ್‌ಡ್ ರಷ್ಯಾ ಸರ್ಕಾರೊದ ಒಟ್ಟುಗ್ ಒಂಜಿ ಒಪ್ಪಂದೊ ಮಲ್ತೆರ್. 1668ಡ್ ಬಾತುರಿನ್‌ದ ಕೋಸಾಕುಲು ಮಾಸ್ಕೋ ವಿರೋಧಿ ದಂಗೆಗ್ ಬೆಂಬಲ ಕೊರ್ಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ === 1669ಡ್ ಹೆಟ್‌ಮಾನ್ ಡೆಮಿಯನ್ ಮ್ನೊಹೊಹ್ರಿಶ್ನಿ ಬಾತುರಿನ್‌ನು ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಕೀಯ ಕೇಂದ್ರೊ ಆದ್ ಆಯ್ಕೆ ಮಲ್ತೆರ್. ಉಂದು ಬಾತುರಿನ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಬೆಳೆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ (1687–1708) ಕಾಲೊಡು ಬಾತುರಿನ್ ಮಸ್ತ್ ಅಭಿವೃದ್ಧಿ ಆಂಡ್. ಪಟ್ಟಣದ ಜನಸಂಖ್ಯೆ ಜಾಸ್ತಿ ಆಯಿನೆರ್ ಬೊಕ್ಕ ಇತ್ತೆ ಮಸ್ತ್ ಚರ್ಚುಲು, ಚಾಪೆಲ್‌ಲು ಬೊಕ್ಕ ರಡ್ಡ್ ಮಠೊಲು ಇತ್ತುಂಡು. ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಜನರಲ್ ಮಿಲಿಟರಿ ಚಾನ್ಸೆಲರಿ ಬೊಕ್ಕ ಜನರಲ್ ಮಿಲಿಟರಿ ಕೋರ್ಟ್ ಕೂಡ ಬಾತುರಿನ್‌ಡ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಮಝೆಪಾ ಬಾತುರಿನ್ ಕೈತಲ್ದ ಹೊಂಚಾರಿವ್ಕಾ ಪ್ರದೇಶೊಡು ಒಂಜಿ ಬಲಿಷ್ಠ ಅರಮನೆ ನಿರ್ಮಿಸಾಯೆರ್. ಉಂದು ಅರೆನ ಆಡಳಿತೊದ ಪ್ರಮುಖ ಕೇಂದ್ರೊ ಆದ್ ಇತ್ತುಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಬಾತುರಿನ್ ದುರಂತ === 1708ಡ್ ಮಹಾ ಉತ್ತರ ಯುದ್ಧದ ಸಮಯೊಡು ಹೆಟ್‌ಮಾನ್ ಇವಾನ್ ಮಝೆಪಾ ರಷ್ಯಾದ ಝಾರ್ ಪೀಟರ್ ದಿ ಗ್ರೇಟ್ಗ್ ವಿರೋಧವಾದ್ ಸ್ವೀಡನ್‌ದ ರಾಜೆ ಚಾರ್ಲ್ಸ್ XIIದ ಒಟ್ಟುಗ್ ಸೇರಾಯೆರ್. ಅಯಿನ ಬುಕ್ಕೊ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ಮುತ್ತಿಗೆ ಪಾಡಿಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1708ರ ನವೆಂಬರ್‌ಡ್ ಅಲೆಕ್ಸಾಂಡರ್ ಮೆನ್ಶಿಕೊವ್ ನೇತೃತ್ವದ ರಷ್ಯನ್ ಪಡೆಲು ಬಾತುರಿನ್‌ನು ವಶೊಗು ದೆತ್ತೊಂದು ಪಟ್ಟಣೊನು ನಾಶ ಮಲ್ತೆರ್. ಈ ಘಟನೆನು ಉಕ್ರೇನಿಯನ್ ಇತಿಹಾಸೊಡು '''ಬಾತುರಿನ್ ದುರಂತ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಈ ದಾಳಿಡ್ ಪಟ್ಟಣೊದ್ ಮಸ್ತ್ ಜನಕುಲು ಬೊಕ್ಕ ರಕ್ಷಣಾ ಪಡೆಲು ತೀರುದು ಪೋಯೆರ್. ಬಾತುರಿನ್ ಸಂಪೂರ್ಣವಾದ್ ನಾಶ ಆಯಿನ ಬುಕ್ಕೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿನ [[ಗ್ಲುಖಿವ್]]ಗು ಬದಲಾಯಿಸಾಯೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> === ಪುನರ್‌ನಿರ್ಮಾಣ ಬೊಕ್ಕ 18ನೇ ಶತಮಾನೊ === ಬಾತುರಿನ್ 1750ರ ದಶಕೊಡು ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್. 1750ಡ್ದ್ 1764ರ ತನಕ ಹೆಟ್‌ಮಾನ್ ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಬಾತುರಿನ್‌ನು ಕುಡ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ರಾಜಧಾನಿ ಆದ್ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ರಝುಮೊವ್ಸ್ಕಿದ್ ಕಾಲೊಡು ಬಾತುರಿನ್‌ಡ್ ಒಂಜಿ ಭವ್ಯ ಅರಮನೆ ನಿರ್ಮಾಣ ಆಂಡ್. ಈ ಅರಮನೆನು ವಾಸ್ತುಶಿಲ್ಪಿ ಆಂಡ್ರೆ ಕ್ವಾಸೊವ್ ವಿನ್ಯಾಸ ಮಲ್ತೆರ್. ಬೊಕ್ಕ ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ಅರೆನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> 1764ಡ್ ರಷ್ಯಾದ ಸಾಮ್ರಾಜ್ಯೊ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಸ್ವಾಯತ್ತತೆನು ರದ್ದ್ ಮಲ್ತಿನ ಬುಕ್ಕೊ ಬಾತುರಿನ್‌ದ ರಾಜಕೀಯ ಮಹತ್ವೊ ಕಡಿಮೆ ಆಂಡ್.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> == ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಪರಂಪರೆ == ಬಾತುರಿನ್‌ಡ್ ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್‌ದ ಕಾಲೊದ ಮಸ್ತ್ ಐತಿಹಾಸಿಕ ಸ್ಮಾರಕೊಲು ಇನಿ ಒರಿದುಂಡು. ಅವುಲೆಡ್ ಮುಖ್ಯವಾದ್ದ್ '''ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ''', '''ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್''', '''ವಾಸಿಲ್ ಕೊಚುಬೈದ ಇಲ್ಲ್''' ಬೊಕ್ಕ ಮಸ್ತ್ ಚರ್ಚುಲೆನ ಅವಶೇಷೊಲುಲಾ ಉಂಡು.<ref>{{cite web |title=Baturyn |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm |publisher=Internet Encyclopedia of Ukraine |access-date=20 August 2026 }}</ref> ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ ಬಾತುರಿನ್‌ದ ಪ್ರಮುಖ ವಾಸ್ತುಶಿಲ್ಪದ ಸ್ಮಾರಕೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಇಜಿ 18ನೇ ಶತಮಾನೊದ್ ನಿರ್ಮಾಣ ಆಯಿನೆರ್ ಬೊಕ್ಕ ನಂತರೊಡು ಚಾರ್ಲ್ಸ್ ಕ್ಯಾಮೆರಾನ್ ನವ-ಶಾಸ್ತ್ರೀಯ ಶೈಲಿಡ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref>{{cite web |title=Baturyn |url=https://en.wikipedia.org/wiki/Baturyn |access-date=20 August 2026 }}</ref> == ಹೆಟ್‌ಮಾನ್ ರಾಜಧಾನಿ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶ == ಬಾತುರಿನ್‌ಡ್ '''ಹೆಟ್‌ಮಾನ್ ರಾಜಧಾನಿ''' ಪಂಡಿನ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶ ಉಂಡು. ಉಂದು ಬಾತುರಿನ್‌ದ ಕೋಸಾಕ್ ಕಾಲೊದ ಐತಿಹಾಸಿಕ ಸ್ಮಾರಕೊಲೆನ್ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನ ಮುಖ್ಯ ಕೇಂದ್ರೊ ಆದುಂಡು. ಸಂರಕ್ಷಣಾ ಪ್ರದೇಶೊಡು ಬಾತುರಿನ್ ಕೋಟೆದ ಸಿಟಾಡೆಲ್, ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ ಅರಮನೆ, ವಾಸಿಲ್ ಕೊಚುಬೈದ ಇಲ್ಲ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು ಸೇರುದು ಉಂಡು. == ಜನಸಂಖ್ಯೆ == 2022ರ ಅಂದಾಜುದ ಪ್ರಕಾರ ಬಾತುರಿನ್‌ದ ಜನಸಂಖ್ಯೆ '''2,406''' ಇತ್ತ್ಂಡ್.<ref>{{cite web |title=Baturyn |url=https://en.wikipedia.org/wiki/Baturyn |access-date=20 August 2026 }}</ref> == ಸಂಬಂಧಿತ ವಿಷಯೊಲು == * ಕೋಸಾಕ್ ಹೆಟ್‌ಮಾನೇಟ್ * ಇವಾನ್ ಮಝೆಪಾ * ಕಿರಿಲೊ ರಝುಮೊವ್ಸ್ಕಿ * ಬಾತುರಿನ್ ದುರಂತ * [[ಗ್ಲುಖಿವ್]] * ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CB%5CA%5CBaturyn.htm Internet Encyclopedia of Ukraine — Baturyn] * [https://www.history.org.ua/?termin=Baturyn_smt Encyclopedia of the History of Ukraine — Baturyn] 0d04mjfb2ipteoo464drtcsmon7djq1 ಟೆಂಪ್ಲೇಟ್:Newcomer task notice 10 28039 366138 2026-07-24T14:08:16Z en>Jonesey95 0 link to the right newcomer task page 366138 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{mbox | type = notice | text = Articles tagged with this template may be '''offered to newcomers''' as a [[:mw:Help:Growth/Tools/Newcomer Tasks|suggested edit task]] on their [[Wikipedia:Newcomer homepage|newcomer homepage]], inviting them to {{#if: {{{1|}}} | {{#switch: {{{1|}}} | copyedit | ce = copyedit | link | links = add links to | tone = revise the tone of | refs | sources = find references for | upd |update = update | expand | stub = expand | #default = {{{1|}}} }} | improve }} the article. }} }}<noinclude>{{Documentation}}</noinclude> qdcs74bbtkt13dtw80z2f9u84to6zhk 366139 366138 2026-08-21T03:52:55Z ChiK 1136 ೧ revisions imported from [[:en:Template:Newcomer_task_notice]] 366138 wikitext text/x-wiki {{ {{{|safesubst:}}}#invoke:Unsubst||date=__DATE__ |$B= {{mbox | type = notice | text = Articles tagged with this template may be '''offered to newcomers''' as a [[:mw:Help:Growth/Tools/Newcomer Tasks|suggested edit task]] on their [[Wikipedia:Newcomer homepage|newcomer homepage]], inviting them to {{#if: {{{1|}}} | {{#switch: {{{1|}}} | copyedit | ce = copyedit | link | links = add links to | tone = revise the tone of | refs | sources = find references for | upd |update = update | expand | stub = expand | #default = {{{1|}}} }} | improve }} the article. }} }}<noinclude>{{Documentation}}</noinclude> qdcs74bbtkt13dtw80z2f9u84to6zhk ಕಚಾನಿವ್ಕಾ ಅರಮನೆ 0 28040 366152 2026-08-21T08:22:07Z Hariprasad Shetty10 6127 ಹೊಸ ಪುಟ: {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್... 366152 wikitext text/x-wiki {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ | architecture_style = ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಬೊಕ್ಕ ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ | construction_start_date = 1770ರ ದಶಕ | embedded = {{Infobox historic site | embed = yes | designation1 = UKRAINE NATIONAL | designation1_offname = {{lang|uk|Палацово-парковий комплекс “Качанівка”}} | designation1_type = ಇತಿಹಾಸ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪ | designation1_number = 250045-Н }} }} '''ಕಚಾನಿವ್ಕಾ ಅರಮನೆ''' ಉಕ್ರೇನ್‌ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡು ಇಪ್ಪುನ ಒಂಜಿ ಪುರಾತನ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಕಟ್ಟಡ. ಉಂದು ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ಇಪ್ಪುನ ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆದುಂಡು. ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಅರಮನೆ-ಪಾರ್ಕ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ.<ref name="ESU">{{cite web |title=Качанівка Національний історико-культурний заповідник |url=https://esu.com.ua/article-11368 |website=Енциклопедія Сучасної України |publisher=Інститут енциклопедичних досліджень НАН України |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಇತಿಹಾಸ === ಕಚಾನಿವ್ಕಾ ಪುದರ್‌ದ್ 18ನೇ ಶತಮಾನೊದ ದಾಖಲೆಲೆಡ್ ತೋಜುದು ಬರ್ಪುಂಡು. 1740ರ ದಶಕೊಡು ಈ ಪ್ರದೇಶೊ ಕೋಚಾನಿವ್ಸ್ಕಿ ಭೂಮಾಲಕರೆನ ಸ್ವಾಮ್ಯೊಡು ಇತ್ತ್ಂಡ್. 1770ರ ದಶಕೊಡು ಎಸ್ಟೇಟ್ ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾಡ್ ಮಲ್ಲ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ನಿರ್ಮಾಣೊ ಸುರು ಆಂಡ್.<ref name="EIU">{{cite web |last=Кот |first=С. І. |title=Качанівка |url=https://resource.history.org.ua/cgi-bin/eiu/history.exe?C21COM=S&I21DBN=EIU&P21DBN=EIU&S21CNR=20&S21COLORTERMS=0&S21FMT=eiu_all&S21P01=0&S21P02=0&S21P03=TRN%3D&S21REF=10&S21STN=1&S21STR=Kachanivka&Z21ID= |website=Енциклопедія історії України |publisher=Інститут історії України НАН України |year=2007 |access-date=21 August 2026 }}</ref> ಅರಮನೆದ ಯೋಜನೆ ಮಾಸ್ಕೋ ವಾಸ್ತುಶಿಲ್ಪಿ ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ಗ್ ಸಂಬಂಧಪಟ್ಟಿದ್ದು, ನಿರ್ಮಾಣ ಕಾರ್ಯೊನು ಉಕ್ರೇನ್‌ದ ವಾಸ್ತುಶಿಲ್ಪಿ ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಮೇಲ್ವಿಚಾರಣೆ ಮಲ್ತೆರ್. ಅರಮನೆದ ಆರಂಭಿಕ ರೂಪೊಡು ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್.<ref name="EIU"/> 1770ರ ದಶಕೊಡು ರುಮ್ಯಾಂತ್ಸೆವ್ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಪಾರ್ಕ್‌ದ ಮೂಲ ವಿನ್ಯಾಸೊಲಾ ರೂಪುಗೊಂಡುದುಂಡು. ಪಿರವುಡು ಬೇತೆ ಮಾಲಕೆರ್ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ವಿಸ್ತಾರ ಮಲ್ತೆರ್.<ref name="ESU"/> === ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊ === 1808ಡ್ ಕಚಾನಿವ್ಕಾ ಗ್ರಿಗೋರಿ ಪೊಚೇಕಾ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಅದಗ ಅರಮನೆಗ್ ರಡ್ಡನೇ ಅಂತಸ್ತು, ಗ್ಯಾಲರಿಲು ಬೊಕ್ಕ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲೆನ್ ಸೇರ್ಸಾಯೆರ್.<ref name="history100">{{cite web |title=Качанівка |url=https://uahistory.co/book/100-great-wonders-of-ukraine/17.php |website=100 Великих чудес України |access-date=21 August 2026 }}</ref> ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ವಾಸ್ತುಶಿಲ್ಪೊ ಬೊಕ್ಕ ಭೂದೃಶ್ಯೊದ ಅಭಿವೃದ್ಧಿಗ್ ಬೊಕ್ಕೊಂಜಿ ಮುಖ್ಯ ಹಂತ ಬತ್ತ್ಂಡ್. ಅರೆನ ಪಿರವುಡು ಎಸ್ಟೇಟ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್.<ref name="EIU"/> === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ === 1824ಡ್ ಕಚಾನಿವ್ಕಾ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸ್ವಾಮ್ಯೊಗು ಬತ್ತ್ಂಡ್. 19ನೇ ಶತಮಾನೊಡು ಈ ಕುಟುಂಬೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅಭಿವೃದ್ಧಿಗ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರಿಯೆರ್.<ref name="EIU"/> ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಸಾಹಿತ್ಯ, ಕಲೆ, ಸಂಗೀತ ಬೊಕ್ಕ ವಿಜ್ಞಾನೊದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದ್ ಬೆಳದ್ಂಡ್. ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ, ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್, ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ, ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್, ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್, ಇಲ್ಯಾ ರೆಪಿನ್ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು ಕಚಾನಿವ್ಕಾಗ್ ಬತ್ತೆರ್.<ref name="EIU"/> === ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊ === 1897ಡ್ ಕಚಾನಿವ್ಕಾ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಆಧುನೀಕರಣೊ ನಡತ್ಂಡ್. ಈ ಅವಧಿಡ್ ಕಟ್ಟಡೊದ ಹೊರಾಂಗಣ ಬೊಕ್ಕ ಒಳಾಂಗಣೊಡು ಕೆಲವು ಬದಲಾವಣೆಲು ಮಲ್ತೆರ್.<ref name="ESU"/> == ವಾಸ್ತುಶಿಲ್ಪ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಇತ್ತೆದ ರೂಪೊ ಒಂಜೇ ಕಾಲೊಡು ನಿರ್ಮಾಣ ಆತುಜಿ. 18ನೇ ಶತಮಾನೊದ ಅಕೆರಿಡ್ದ್ 20ನೇ ಶತಮಾನೊದ ಸುರುತ ತನಕ ನಡತಿನ ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್.<ref name="ESU"/> ಅರಮನೆದ ಮೂಲ ನಿರ್ಮಾಣೊ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಶೈಲಿಡ್ ನಡತ್ಂಡ್. ಪಿರವುಡು ಮಾಲಕೆರ್ ಅರಮನೆನ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಕಾರಣೊಡು ಕಟ್ಟಡೊಡು ಶಾಸ್ತ್ರೀಯ ಬೊಕ್ಕ ನವಶಾಸ್ತ್ರೀಯ ಲಕ್ಷಣೊಲುಲಾ ತೋಜಿದ್ ಬರ್ಪುಂಡು.<ref name="100wonders"/> ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊಗು ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಗ್ಯಾಲರಿಲು ಸೇರ್ಸಾಯಿನಿ. ಸಮುಚ್ಚಯೊದ ವಿನ್ಯಾಸೊ ಅರಮನೆ, ಸೇವಾ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಪಾರ್ಕ್‌ನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ಮಾರ್ಪಾಡು ಆತುಂಡು.<ref name="ESU"/> == ಅರಮನೆದ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು == ಅರಮನೆದ ಮುಖ್ಯ ಕಟ್ಟಡೊದ ರಡ್ಡ್ ಬದಿಡ್ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಅತ್ತುಂಡ ಫ್ಲಿಗಲ್ ಉಂಡು. ಈ ಕಟ್ಟಡೊಲು ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ದೆರ್. ಎಸ್ಟೇಟ್‌ದ ಅಭಿವೃದ್ಧಿ ನಡತಿನ ಕಾಲೊಡು ಸೇವಾ ಕಟ್ಟಡೊಲು, ಗೃಹೊಲು ಬೊಕ್ಕ ಬೇತೆ ಉಪಯೋಗೊಲೆಗ್ ಬೇಕಾಯಿನ ರಚನೆಲು ಸೇರ್ಸಾಯಿನಿ. ಇವುಲು ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಐತಿಹಾಸಿಕ ಸ್ವರೂಪೊನು ಪೂರೈಸುಂಡು.<ref name="ESU"/> == ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಡು ಒಂಜಿ ಐತಿಹಾಸಿಕ ಚರ್ಚ್‌ಲಾ ಉಂಡು. 19ನೇ ಶತಮಾನೊದ ಆರಂಭೊಡು ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಉಂದು ಸಮುಚ್ಚಯೊದ ವಾಸ್ತುಶಿಲ್ಪೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆಂಡ್.<ref name="ESU"/> ಚರ್ಚ್ ಬೊಕ್ಕ ಅರಮನೆದ ಸಂಬಂಧೊ ಕಚಾನಿವ್ಕಾ ಎಸ್ಟೇಟ್‌ದ ಧಾರ್ಮಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಜೀವನೊನು ತೋಜಾಯೆರುಂಡು. == ಪಾರ್ಕ್ == ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್ ಅರಮನೆದತೇ ಪ್ರಮುಖ ಪರಂಪರೆ ಆದುಂಡು. ಪಾರ್ಕ್‌ದ ನಿರ್ಮಾಣೊ 18ನೇ ಶತಮಾನೊದ ಅಕೇರಿಡ್ ಸುರು ಆಯಿನಿ ಬೊಕ್ಕ ಪಿರಾವುಡು ಅವೆತೊ ತಲೆಮಾರುದ ಮಾಲಕೆರ್ ಅವೆನ್ ಅಭಿವೃದ್ಧಿ ಮಲ್ತೆರ್.<ref name="OfficialReserve">{{cite web |title=Про організатора — Національний історико-культурний заповідник «Качанівка» |url=https://kachanivka.event.net.ua/site/about |website=Національний історико-культурний заповідник «Качанівка» |access-date=21 August 2026 }}</ref> ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ವಿಸ್ತೀರ್ಣೊ ಸುಮಾರು '''568.63 ಹೆಕ್ಟೇರ್'''. ಇಂಚ ಸುಮಾರು '''125.5 ಹೆಕ್ಟೇರ್''' ನೀರ್ ದ ಪ್ರದೇಶೊ ಉಂಡು. ಪಾರ್ಕ್ ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ವಿಸ್ತಾರ ಆದುಂಡು.<ref name="ParkArea">{{cite web |title=Качанівка |url=https://secinfchounb.blogspot.com/2012/03/2011-30.html |website=Чернігівська обласна універсальна наукова бібліотека |access-date=21 August 2026 }}</ref> ಪಾರ್ಕ್‌ಡು ಮಲ್ಲ ಮರೊಲೆನ ಗುಂಪು, ಜಲಾಶಯೊಲು, ತಾದಿಲು, ಭೂದೃಶ್ಯ ಪ್ರದೇಶೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು ಉಂಡು. ಪ್ರಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ವಿನ್ಯಾಸೊ ಕಚಾನಿವ್ಕಾಗ್ ವಿಶೇಷ ಮಹತ್ವ ಕೊರ್ತುಂಡು.<ref name="OfficialReserve"/> == ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು == ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಚರ್ಚ್‌ದ ಹೊರತು ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲುಲಾ ಉಂಡು. ಅವುಲೆಡ್: * ಸೇವಾ ಕಟ್ಟಡೊಲು * ಫ್ಲಿಗಲ್ * ಆರೆಂಜರಿ * ಏವಿಯರಿ * ನೀರಿನ ಗೋಪುರ * ಉದ್ಯಾನೊದ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು * ಮಂಟಪೊಲು * ಪಾರ್ಕ್‌ದ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಕಚಾನಿವ್ಕಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಸಮುಚ್ಚಯೊಡು ಮಸ್ತ್ ಕಟ್ಟಡೊಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು.<ref name="OfficialReserve"/> == ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಂಗ್ರಹೊ == ವಾಸಿಲ್ ಟಾರ್ನೋವ್ಸ್ಕಿ (ಕಿರಿಯ) ಉಕ್ರೇನ್‌ದ ಇತಿಹಾಸ ಬೊಕ್ಕ ಸಂಸ್ಕೃತಿದ ಬಗ್ಗೆ ವಿಶೇಷ ಆಸಕ್ತಿ ತೋಜಾದೆರ್. ಆರ್ ಕೋಸಾಕ್ ಇತಿಹಾಸೊಗು ಸಂಬಂಧಪಡುದಿನ ಆಯುಧೊಲು, ದಾಖಲೆಲು, ಕಲಾಕೃತಿಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ವಸ್ತುಲೆನ ಮಲ್ಲ ಸಂಗ್ರಹೊನು ನಿರ್ಮಾಣ ಮಲ್ದೆರ್.<ref name="EIU"/> ಈ ಸಂಗ್ರಹೊ ಉಕ್ರೇನ್‌ದ ಹೆಟ್ಮನ್ ಬೊಕ್ಕ ಕೋಸಾಕ್ ಪರಂಪರೆದ ಅಧ್ಯಯನೊಗು ಮಹತ್ವದ ಕೊಡುಗೆ ಕೊರ್ತುಂಡು. ಪಿರವುದ ಸಂಗ್ರಹೊದ ಕೆಲವು ಭಾಗೊಲೆನ್ ಸಾರ್ವಜನಿಕ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲೆಗ್ ವರ್ಗಾವಣೆ ಮಲ್ತುದೆರ್. == ಸಾಹಿತ್ಯ ಬೊಕ್ಕ ಕಲೆ == 19ನೇ ಶತಮಾನೊಡು ಕಚಾನಿವ್ಕಾ ಒಂಜಿ ಪ್ರಮುಖ ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾತ್ಮಕ ಕೇಂದ್ರ ಆಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಅವೆತೋ ಪ್ರಸಿದ್ಧ ಬರಹಗಾರೆರ್, ಕಲಾವಿದೆರೆರ್ ಬೊಕ್ಕ ಸಂಗೀತಗಾರೆರೆಗ್ ಆಶ್ರಯ ಕೊರ್ತೆರ್.<ref name="EIU"/> ಕಚಾನಿವ್ಕಾಗ್ ಭೇಟಿ ಕೊರಿನ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು: * ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ * ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್ * ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ * ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್ * ವ್ಲಾಡಿಮಿರ್ ಮಕೋವ್ಸ್ಕಿ * ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್ * ಇಲ್ಯಾ ರೆಪಿನ್ * ವಿಕ್ಟರ್ ಶ್ಟೆನ್‌ಬರ್ಗ್ * ಮೈಖೈಲೊ ಮಕ್ಸಿಮೊವಿಚ್ * ದಿಮಿತ್ರೊ ಯವೊರ್ನಿತ್ಸ್ಕಿ <ref name="EIU"/> ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ ಕಚಾನಿವ್ಕಾದಲ್ಲಿ ವಾಸ್ತವ್ಯ ಹೂಡುದು ಸಂಗೀತ ಕೃತಿಕುಲೆನ ಮಿತ್ತ್ ಕೆಲಸ ಮಲ್ತೆರ್. ಅರೆನ ಕಚಾನಿವ್ಕಾ ವಾಸ್ತವ್ಯೊ ಉಕ್ರೇನ್‌ದ ಸಂಗೀತ ಇತಿಹಾಸೊಡು ಗಮನಾರ್ಹ ಘಟನೆ ಆದುಂಡು.<ref name="EIU"/> == 20ನೇ ಶತಮಾನ == 1918ಡ್ ಸೋವಿಯತ್ ಆಡಳಿತೊ ಬತ್ತಿನ ಪಿರಾವುಡು ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಕರಣ ಆಯಿನಿ. 1918–1920ರ ರಾಜಕೀಯ ಬದಲಾವಣೆಲೆನ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ಆಡಳಿತೊ ಹಲವು ಬಾರಿ ಬದಲಾತುಂಡು.<ref name="ESU"/> 1941ರ ಸೆಪ್ಟೆಂಬರ್‌ಡ್ದ್ 1943ರ ಸೆಪ್ಟೆಂಬರ್ ತನಕ್ ಕಚಾನಿವ್ಕಾ ಜರ್ಮನ್ ಆಕ್ರಮಣೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್.<ref name="ESU"/> ಸೋವಿಯತ್ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಎಸ್ಟೇಟ್‌ದ ಕಟ್ಟಡೊಲೆನ್ ಬೇತೆ ಬೇತೆ ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್. ಈ ಬಳಕೆಲೆನ ಕಾರಣೊಡು ಸಮುಚ್ಚಯೊದ ಕೆಲವು ಭಾಗೊಲೆಗ್ ಹಾನಿ ಲಾ ಆತುಂಡು.<ref name="OfficialReserve"/> == ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ == 24 ನವೆಂಬರ್ 1981ಡ್ ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಆಧಾರೊಡು '''ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ''' ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್. ಈ ನಿರ್ಧಾರೊದ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನವು.<ref name="OfficialReserve"/> 1981ಡ್ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಸ್ಥಾಪನೆ ಆಯಿನ ಪಿರವುಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಆದ್ ಗುರುತುಗು ಬೈದುಂಡು.<ref name="ESU"/> == ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ == 27 ಫೆಬ್ರವರಿ 2001ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷ ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ಆರ್ ಪಿದಾಡುಪಾಯಿನ №118/2001 ಆದೇಶೊದ ಮೇರೆಡ್ ಕಚಾನಿವ್ಕಾ ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಗು '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ'''ದ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="President">{{cite web |title=Про надання статусу національного Державному історико-культурному заповіднику «Качанівка» |url=https://www.president.gov.ua/documents/1182001-543 |website=Президент України |date=27 February 2001 |access-date=21 August 2026 }}</ref> ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಅಮೂಲ್ಯ ವಸ್ತುಲೆನ್ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಜೊತೊಗು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಮಹತ್ವೊನು ಉಲ್ಲೇಕ ಮಲ್ತುಂಡು.<ref name="President"/> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಎಡದಂಡೆ ಪ್ರದೇಶೊಡು ಉಳಿದಿರುವ ಪ್ರಮುಖ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಅರಮನೆ ವಾಸ್ತುಶಿಲ್ಪೊ, ಉದ್ಯಾನಕಲೆ, ಸಾಹಿತ್ಯ, ಸಂಗೀತ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸಾಂಸ್ಕೃತಿಕ ಇತಿಹಾಸೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ತಾಣ ಆಂಡ್.<ref name="ESU"/> ಆಧುನಿಕ ಉಕ್ರೇನ್ ದ ವಿಶ್ವಕೋಶದ ಪ್ರಕಾರ ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಎಸ್ಟೇಟ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. ಉಕ್ರೇನ್‌ಡು ಅರೆನ ಐತಿಹಾಸಿಕ ಸಮಗ್ರತೊನು ಒರಿಪಾದಿನ ಅಪರೂಪದ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಆದುಂಡು.<ref name="ESU"/> 19ನೇ ಶತಮಾನೊಡು ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಬುದ್ಧಿಜೀವಿ, ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾವಿದರೆನ ಭೇಟಿದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದುಂಡು.<ref name="EIU"/> == ಪ್ರವಾಸೋದ್ಯಮ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಇತ್ತೆ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪ್ರವಾಸೋದ್ಯಮೊದ ಪ್ರಮುಖ ತಾಣ ಆದುಂಡು. ಅರಮನೆ, ವಿಶಾಲ ಪಾರ್ಕ್, ಐತಿಹಾಸಿಕ ಚರ್ಚ್, ಪಾರ್ಕ್‌ದ ರಚನೆಲು ಬೊಕ್ಕ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ಪ್ರವಾಸಿಗರೆಗ್ ಪ್ರಮುಖ ಆಕರ್ಷಣೆಲು ಆದುಂಡು.<ref name="OfficialReserve"/> ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಕಾರಣೊಡು ಕಚಾನಿವ್ಕಾ ಕೇವಲ ಒಂಜಿ ಕಟ್ಟಡ ಅತ್ತಂದೆ ಒಂಜಿ ಸಮಗ್ರ ಸಾಂಸ್ಕೃತಿಕ ಭೂದೃಶ್ಯ ತಾಣ ಆದುಂಡು. == ಸಂರಕ್ಷಣೆ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಮುಖ್ಯ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್, ಚರ್ಚ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಬಳಕೆ ಆದುಂಡು.<ref name="President"/> 2001ರ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡುಲಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಪುನರುಜ್ಜೀವನೊಗು ಹೆಚ್ಚುವರಿ ಕ್ರಮೊಲೆನ್ ಮಲ್ಪುನ ಅಗತ್ಯೊನು ಸೂಚಿಸಾದೆರ್.<ref name="President"/> == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://kachanivka.event.net.ua/site/about ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * [https://kachanivka.in.ua/ ಕಚಾನಿವ್ಕಾ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kachanivka National Historical and Cultural Reserve}} [[Category:ಉಕ್ರೇನ್‌ದ ಅರಮನೆಲು]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:1770ರ ದಶಕೊದ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಉದ್ಯಾನೊಲು]] 5g6l17g7jtdpbkoi58vgo4x1qwf51ff 366154 366152 2026-08-21T08:23:39Z Hariprasad Shetty10 6127 /* ವಾಸ್ತುಶಿಲ್ಪ */ 366154 wikitext text/x-wiki {{Infobox building | name = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ | native_name = Качанівський палац | native_name_lang = uk | image = Kachanivka_panorama.jpg | caption = ಕಚಾನಿವ್ಕಾ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ | location = ಕಚಾನಿವ್ಕಾ, ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್, ಉಕ್ರೇನ್ | architect = ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್, ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ | architecture_style = ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಬೊಕ್ಕ ಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ | construction_start_date = 1770ರ ದಶಕ | embedded = {{Infobox historic site | embed = yes | designation1 = UKRAINE NATIONAL | designation1_offname = {{lang|uk|Палацово-парковий комплекс “Качанівка”}} | designation1_type = ಇತಿಹಾಸ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪ | designation1_number = 250045-Н }} }} '''ಕಚಾನಿವ್ಕಾ ಅರಮನೆ''' ಉಕ್ರೇನ್‌ದ ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್ಡು ಇಪ್ಪುನ ಒಂಜಿ ಪುರಾತನ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಕಟ್ಟಡ. ಉಂದು ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ಇಪ್ಪುನ ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆದುಂಡು. ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಅರಮನೆ-ಪಾರ್ಕ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ.<ref name="ESU">{{cite web |title=Качанівка Національний історико-культурний заповідник |url=https://esu.com.ua/article-11368 |website=Енциклопедія Сучасної України |publisher=Інститут енциклопедичних досліджень НАН України |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಇತಿಹಾಸ === ಕಚಾನಿವ್ಕಾ ಪುದರ್‌ದ್ 18ನೇ ಶತಮಾನೊದ ದಾಖಲೆಲೆಡ್ ತೋಜುದು ಬರ್ಪುಂಡು. 1740ರ ದಶಕೊಡು ಈ ಪ್ರದೇಶೊ ಕೋಚಾನಿವ್ಸ್ಕಿ ಭೂಮಾಲಕರೆನ ಸ್ವಾಮ್ಯೊಡು ಇತ್ತ್ಂಡ್. 1770ರ ದಶಕೊಡು ಎಸ್ಟೇಟ್ ಪ್ಯೋತ್ರ್ ರುಮ್ಯಾಂತ್ಸೆವ್-ಝದುನೈಸ್ಕಿ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾಡ್ ಮಲ್ಲ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ನಿರ್ಮಾಣೊ ಸುರು ಆಂಡ್.<ref name="EIU">{{cite web |last=Кот |first=С. І. |title=Качанівка |url=https://resource.history.org.ua/cgi-bin/eiu/history.exe?C21COM=S&I21DBN=EIU&P21DBN=EIU&S21CNR=20&S21COLORTERMS=0&S21FMT=eiu_all&S21P01=0&S21P02=0&S21P03=TRN%3D&S21REF=10&S21STN=1&S21STR=Kachanivka&Z21ID= |website=Енциклопедія історії України |publisher=Інститут історії України НАН України |year=2007 |access-date=21 August 2026 }}</ref> ಅರಮನೆದ ಯೋಜನೆ ಮಾಸ್ಕೋ ವಾಸ್ತುಶಿಲ್ಪಿ ಕಾರ್ಲ್ ಬ್ಲಾಂಕ್‌ಗ್ ಸಂಬಂಧಪಟ್ಟಿದ್ದು, ನಿರ್ಮಾಣ ಕಾರ್ಯೊನು ಉಕ್ರೇನ್‌ದ ವಾಸ್ತುಶಿಲ್ಪಿ ಮ್ಯಾಕ್ಸಿಮ್ ಮೊಸ್ಚೆಪಾನೊವ್ ಮೇಲ್ವಿಚಾರಣೆ ಮಲ್ತೆರ್. ಅರಮನೆದ ಆರಂಭಿಕ ರೂಪೊಡು ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಲಕ್ಷಣೊಲು ಇತ್ತ್ಂಡ್.<ref name="EIU"/> 1770ರ ದಶಕೊಡು ರುಮ್ಯಾಂತ್ಸೆವ್ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಪಾರ್ಕ್‌ದ ಮೂಲ ವಿನ್ಯಾಸೊಲಾ ರೂಪುಗೊಂಡುದುಂಡು. ಪಿರವುಡು ಬೇತೆ ಮಾಲಕೆರ್ ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ವಿಸ್ತಾರ ಮಲ್ತೆರ್.<ref name="ESU"/> === ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊ === 1808ಡ್ ಕಚಾನಿವ್ಕಾ ಗ್ರಿಗೋರಿ ಪೊಚೇಕಾ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಅದಗ ಅರಮನೆಗ್ ರಡ್ಡನೇ ಅಂತಸ್ತು, ಗ್ಯಾಲರಿಲು ಬೊಕ್ಕ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲೆನ್ ಸೇರ್ಸಾಯೆರ್.<ref name="history100">{{cite web |title=Качанівка |url=https://uahistory.co/book/100-great-wonders-of-ukraine/17.php |website=100 Великих чудес України |access-date=21 August 2026 }}</ref> ಪೊಚೇಕಾ ಕುಟುಂಬೊದ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ವಾಸ್ತುಶಿಲ್ಪೊ ಬೊಕ್ಕ ಭೂದೃಶ್ಯೊದ ಅಭಿವೃದ್ಧಿಗ್ ಬೊಕ್ಕೊಂಜಿ ಮುಖ್ಯ ಹಂತ ಬತ್ತ್ಂಡ್. ಅರೆನ ಪಿರವುಡು ಎಸ್ಟೇಟ್ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್.<ref name="EIU"/> === ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊ === 1824ಡ್ ಕಚಾನಿವ್ಕಾ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸ್ವಾಮ್ಯೊಗು ಬತ್ತ್ಂಡ್. 19ನೇ ಶತಮಾನೊಡು ಈ ಕುಟುಂಬೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಬೇತೆ ಕಟ್ಟಡೊಲೆನ ಅಭಿವೃದ್ಧಿಗ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರಿಯೆರ್.<ref name="EIU"/> ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಸಾಹಿತ್ಯ, ಕಲೆ, ಸಂಗೀತ ಬೊಕ್ಕ ವಿಜ್ಞಾನೊದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದ್ ಬೆಳದ್ಂಡ್. ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ, ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್, ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ, ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್, ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್, ಇಲ್ಯಾ ರೆಪಿನ್ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು ಕಚಾನಿವ್ಕಾಗ್ ಬತ್ತೆರ್.<ref name="EIU"/> === ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊ === 1897ಡ್ ಕಚಾನಿವ್ಕಾ ಹರಿತೋನೆಂಕೊ ಕುಟುಂಬೊದ ಕೈಕ್ ಬತ್ತ್ಂಡ್. ಅರೆನ ಕಾಲೊಡು ಅರಮನೆದ ಪುನರ್‌ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಆಧುನೀಕರಣೊ ನಡತ್ಂಡ್. ಈ ಅವಧಿಡ್ ಕಟ್ಟಡೊದ ಹೊರಾಂಗಣ ಬೊಕ್ಕ ಒಳಾಂಗಣೊಡು ಕೆಲವು ಬದಲಾವಣೆಲು ಮಲ್ತೆರ್.<ref name="ESU"/> == ವಾಸ್ತುಶಿಲ್ಪ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆದ ಇತ್ತೆದ ರೂಪೊ ಒಂಜೇ ಕಾಲೊಡು ನಿರ್ಮಾಣ ಆತುಜಿ. 18ನೇ ಶತಮಾನೊದ ಅಕೆರಿಡ್ದ್ 20ನೇ ಶತಮಾನೊದ ಸುರುತ ತನಕ ನಡತಿನ ನಿರ್ಮಾಣ ಬೊಕ್ಕ ಪುನರ್‌ನಿರ್ಮಾಣ ಆಂಡ್.<ref name="ESU"/> ಅರಮನೆದ ಮೂಲ ನಿರ್ಮಾಣೊ ಪ್ಸೆವ್ಡೊ-ಗಾಥಿಕ್ ಶೈಲಿಡ್ ನಡತ್ಂಡ್. ಪಿರವುಡು ಮಾಲಕೆರ್ ಅರಮನೆನ್ ಪುನರ್‌ನಿರ್ಮಾಣ ಮಲ್ತಿನ ಕಾರಣೊಡು ಕಟ್ಟಡೊಡು ಶಾಸ್ತ್ರೀಯ ಬೊಕ್ಕ ನವಶಾಸ್ತ್ರೀಯ ಲಕ್ಷಣೊಲುಲಾ ತೋಜಿದ್ ಬರ್ಪುಂಡು. ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊಗು ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಗ್ಯಾಲರಿಲು ಸೇರ್ಸಾಯಿನಿ. ಸಮುಚ್ಚಯೊದ ವಿನ್ಯಾಸೊ ಅರಮನೆ, ಸೇವಾ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ಪಾರ್ಕ್‌ನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ಮಾರ್ಪಾಡು ಆತುಂಡು.<ref name="ESU"/> == ಅರಮನೆದ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು == ಅರಮನೆದ ಮುಖ್ಯ ಕಟ್ಟಡೊದ ರಡ್ಡ್ ಬದಿಡ್ ಪಾರ್ಶ್ವ ಕಟ್ಟಡೊಲು ಅತ್ತುಂಡ ಫ್ಲಿಗಲ್ ಉಂಡು. ಈ ಕಟ್ಟಡೊಲು ಅರಮನೆದ ಕೇಂದ್ರ ಭಾಗೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ರೀತಿಡ್ ವಿನ್ಯಾಸ ಮಲ್ದೆರ್. ಎಸ್ಟೇಟ್‌ದ ಅಭಿವೃದ್ಧಿ ನಡತಿನ ಕಾಲೊಡು ಸೇವಾ ಕಟ್ಟಡೊಲು, ಗೃಹೊಲು ಬೊಕ್ಕ ಬೇತೆ ಉಪಯೋಗೊಲೆಗ್ ಬೇಕಾಯಿನ ರಚನೆಲು ಸೇರ್ಸಾಯಿನಿ. ಇವುಲು ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಐತಿಹಾಸಿಕ ಸ್ವರೂಪೊನು ಪೂರೈಸುಂಡು.<ref name="ESU"/> == ಸೇಂಟ್ ಜಾರ್ಜ್ ಚರ್ಚ್ == ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಡು ಒಂಜಿ ಐತಿಹಾಸಿಕ ಚರ್ಚ್‌ಲಾ ಉಂಡು. 19ನೇ ಶತಮಾನೊದ ಆರಂಭೊಡು ಚರ್ಚ್‌ದ ನಿರ್ಮಾಣೊ ನಡತ್ಂಡ್. ಉಂದು ಸಮುಚ್ಚಯೊದ ವಾಸ್ತುಶಿಲ್ಪೊದ ಒಂಜಿ ಪ್ರಮುಖ ಭಾಗ ಆಂಡ್.<ref name="ESU"/> ಚರ್ಚ್ ಬೊಕ್ಕ ಅರಮನೆದ ಸಂಬಂಧೊ ಕಚಾನಿವ್ಕಾ ಎಸ್ಟೇಟ್‌ದ ಧಾರ್ಮಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಜೀವನೊನು ತೋಜಾಯೆರುಂಡು. == ಪಾರ್ಕ್ == ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್ ಅರಮನೆದತೇ ಪ್ರಮುಖ ಪರಂಪರೆ ಆದುಂಡು. ಪಾರ್ಕ್‌ದ ನಿರ್ಮಾಣೊ 18ನೇ ಶತಮಾನೊದ ಅಕೇರಿಡ್ ಸುರು ಆಯಿನಿ ಬೊಕ್ಕ ಪಿರಾವುಡು ಅವೆತೊ ತಲೆಮಾರುದ ಮಾಲಕೆರ್ ಅವೆನ್ ಅಭಿವೃದ್ಧಿ ಮಲ್ತೆರ್.<ref name="OfficialReserve">{{cite web |title=Про організатора — Національний історико-культурний заповідник «Качанівка» |url=https://kachanivka.event.net.ua/site/about |website=Національний історико-культурний заповідник «Качанівка» |access-date=21 August 2026 }}</ref> ಕಚಾನಿವ್ಕಾ ಪಾರ್ಕ್‌ದ ವಿಸ್ತೀರ್ಣೊ ಸುಮಾರು '''568.63 ಹೆಕ್ಟೇರ್'''. ಇಂಚ ಸುಮಾರು '''125.5 ಹೆಕ್ಟೇರ್''' ನೀರ್ ದ ಪ್ರದೇಶೊ ಉಂಡು. ಪಾರ್ಕ್ ಸ್ಮೋಶ್ ತುದೆತ್ತ ದಂಡೆಡ್ ವಿಸ್ತಾರ ಆದುಂಡು.<ref name="ParkArea">{{cite web |title=Качанівка |url=https://secinfchounb.blogspot.com/2012/03/2011-30.html |website=Чернігівська обласна універсальна наукова бібліотека |access-date=21 August 2026 }}</ref> ಪಾರ್ಕ್‌ಡು ಮಲ್ಲ ಮರೊಲೆನ ಗುಂಪು, ಜಲಾಶಯೊಲು, ತಾದಿಲು, ಭೂದೃಶ್ಯ ಪ್ರದೇಶೊಲು ಬೊಕ್ಕ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು ಉಂಡು. ಪ್ರಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ವಿನ್ಯಾಸೊ ಕಚಾನಿವ್ಕಾಗ್ ವಿಶೇಷ ಮಹತ್ವ ಕೊರ್ತುಂಡು.<ref name="OfficialReserve"/> == ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲು == ಕಚಾನಿವ್ಕಾ ಸಮುಚ್ಚಯೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಚರ್ಚ್‌ದ ಹೊರತು ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲುಲಾ ಉಂಡು. ಅವುಲೆಡ್: * ಸೇವಾ ಕಟ್ಟಡೊಲು * ಫ್ಲಿಗಲ್ * ಆರೆಂಜರಿ * ಏವಿಯರಿ * ನೀರಿನ ಗೋಪುರ * ಉದ್ಯಾನೊದ ಅಲಂಕಾರಿಕ ಕಟ್ಟಡೊಲು * ಮಂಟಪೊಲು * ಪಾರ್ಕ್‌ದ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಕಚಾನಿವ್ಕಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ವಾಸ್ತುಶಿಲ್ಪ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಸಮುಚ್ಚಯೊಡು ಮಸ್ತ್ ಕಟ್ಟಡೊಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು.<ref name="OfficialReserve"/> == ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಂಗ್ರಹೊ == ವಾಸಿಲ್ ಟಾರ್ನೋವ್ಸ್ಕಿ (ಕಿರಿಯ) ಉಕ್ರೇನ್‌ದ ಇತಿಹಾಸ ಬೊಕ್ಕ ಸಂಸ್ಕೃತಿದ ಬಗ್ಗೆ ವಿಶೇಷ ಆಸಕ್ತಿ ತೋಜಾದೆರ್. ಆರ್ ಕೋಸಾಕ್ ಇತಿಹಾಸೊಗು ಸಂಬಂಧಪಡುದಿನ ಆಯುಧೊಲು, ದಾಖಲೆಲು, ಕಲಾಕೃತಿಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ವಸ್ತುಲೆನ ಮಲ್ಲ ಸಂಗ್ರಹೊನು ನಿರ್ಮಾಣ ಮಲ್ದೆರ್.<ref name="EIU"/> ಈ ಸಂಗ್ರಹೊ ಉಕ್ರೇನ್‌ದ ಹೆಟ್ಮನ್ ಬೊಕ್ಕ ಕೋಸಾಕ್ ಪರಂಪರೆದ ಅಧ್ಯಯನೊಗು ಮಹತ್ವದ ಕೊಡುಗೆ ಕೊರ್ತುಂಡು. ಪಿರವುದ ಸಂಗ್ರಹೊದ ಕೆಲವು ಭಾಗೊಲೆನ್ ಸಾರ್ವಜನಿಕ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲೆಗ್ ವರ್ಗಾವಣೆ ಮಲ್ತುದೆರ್. == ಸಾಹಿತ್ಯ ಬೊಕ್ಕ ಕಲೆ == 19ನೇ ಶತಮಾನೊಡು ಕಚಾನಿವ್ಕಾ ಒಂಜಿ ಪ್ರಮುಖ ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾತ್ಮಕ ಕೇಂದ್ರ ಆಂಡ್. ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಅವೆತೋ ಪ್ರಸಿದ್ಧ ಬರಹಗಾರೆರ್, ಕಲಾವಿದೆರೆರ್ ಬೊಕ್ಕ ಸಂಗೀತಗಾರೆರೆಗ್ ಆಶ್ರಯ ಕೊರ್ತೆರ್.<ref name="EIU"/> ಕಚಾನಿವ್ಕಾಗ್ ಭೇಟಿ ಕೊರಿನ ಪ್ರಸಿದ್ಧ ವ್ಯಕ್ತಿಲು: * ತಾರಸ್ ಶೆವ್ಚೆಂಕೊ * ನಿಕೊಲಾಯ್ ಗೊಗೋಲ್ * ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ * ಮಾರ್ಕೊ ವೊವ್ಚೋಕ್ * ವ್ಲಾಡಿಮಿರ್ ಮಕೋವ್ಸ್ಕಿ * ಮಿಖಾಯಿಲ್ ವ್ರುಬೆಲ್ * ಇಲ್ಯಾ ರೆಪಿನ್ * ವಿಕ್ಟರ್ ಶ್ಟೆನ್‌ಬರ್ಗ್ * ಮೈಖೈಲೊ ಮಕ್ಸಿಮೊವಿಚ್ * ದಿಮಿತ್ರೊ ಯವೊರ್ನಿತ್ಸ್ಕಿ <ref name="EIU"/> ಮಿಖಾಯಿಲ್ ಗ್ಲಿಂಕಾ ಕಚಾನಿವ್ಕಾದಲ್ಲಿ ವಾಸ್ತವ್ಯ ಹೂಡುದು ಸಂಗೀತ ಕೃತಿಕುಲೆನ ಮಿತ್ತ್ ಕೆಲಸ ಮಲ್ತೆರ್. ಅರೆನ ಕಚಾನಿವ್ಕಾ ವಾಸ್ತವ್ಯೊ ಉಕ್ರೇನ್‌ದ ಸಂಗೀತ ಇತಿಹಾಸೊಡು ಗಮನಾರ್ಹ ಘಟನೆ ಆದುಂಡು.<ref name="EIU"/> == 20ನೇ ಶತಮಾನ == 1918ಡ್ ಸೋವಿಯತ್ ಆಡಳಿತೊ ಬತ್ತಿನ ಪಿರಾವುಡು ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಕರಣ ಆಯಿನಿ. 1918–1920ರ ರಾಜಕೀಯ ಬದಲಾವಣೆಲೆನ ಕಾಲೊಡು ಎಸ್ಟೇಟ್‌ದ ಆಡಳಿತೊ ಹಲವು ಬಾರಿ ಬದಲಾತುಂಡು.<ref name="ESU"/> 1941ರ ಸೆಪ್ಟೆಂಬರ್‌ಡ್ದ್ 1943ರ ಸೆಪ್ಟೆಂಬರ್ ತನಕ್ ಕಚಾನಿವ್ಕಾ ಜರ್ಮನ್ ಆಕ್ರಮಣೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್.<ref name="ESU"/> ಸೋವಿಯತ್ ಕಾಲೊಡು ಅರಮನೆ ಬೊಕ್ಕ ಎಸ್ಟೇಟ್‌ದ ಕಟ್ಟಡೊಲೆನ್ ಬೇತೆ ಬೇತೆ ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಮಲ್ತೆರ್. ಈ ಬಳಕೆಲೆನ ಕಾರಣೊಡು ಸಮುಚ್ಚಯೊದ ಕೆಲವು ಭಾಗೊಲೆಗ್ ಹಾನಿ ಲಾ ಆತುಂಡು.<ref name="OfficialReserve"/> == ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ == 24 ನವೆಂಬರ್ 1981ಡ್ ಕಚಾನಿವ್ಕಾ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊದ ಆಧಾರೊಡು '''ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ''' ಸ್ಥಾಪನೆ ಮಲ್ತೆರ್. ಈ ನಿರ್ಧಾರೊದ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ ಮಲ್ಪುನವು.<ref name="OfficialReserve"/> 1981ಡ್ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಸ್ಥಾಪನೆ ಆಯಿನ ಪಿರವುಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ತಾಣೊಲೆಡ್ ಒಂಜಿ ಆದ್ ಗುರುತುಗು ಬೈದುಂಡು.<ref name="ESU"/> == ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ == 27 ಫೆಬ್ರವರಿ 2001ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷ ಲಿಯೊನಿಡ್ ಕುಚ್ಮಾ ಆರ್ ಪಿದಾಡುಪಾಯಿನ №118/2001 ಆದೇಶೊದ ಮೇರೆಡ್ ಕಚಾನಿವ್ಕಾ ರಾಜ್ಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಗು '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ'''ದ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="President">{{cite web |title=Про надання статусу національного Державному історико-культурному заповіднику «Качанівка» |url=https://www.president.gov.ua/documents/1182001-543 |website=Президент України |date=27 February 2001 |access-date=21 August 2026 }}</ref> ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡು ಕಚಾನಿವ್ಕಾ ಇತಿಹಾಸ, ಸಂಸ್ಕೃತಿ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಅಮೂಲ್ಯ ವಸ್ತುಲೆನ್ ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಜೊತೊಗು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಮಹತ್ವೊನು ಉಲ್ಲೇಕ ಮಲ್ತುಂಡು.<ref name="President"/> == ಸಾಂಸ್ಕೃತಿಕ ಮಹತ್ವ == ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಎಡದಂಡೆ ಪ್ರದೇಶೊಡು ಉಳಿದಿರುವ ಪ್ರಮುಖ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಅರಮನೆ ವಾಸ್ತುಶಿಲ್ಪೊ, ಉದ್ಯಾನಕಲೆ, ಸಾಹಿತ್ಯ, ಸಂಗೀತ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸಾಂಸ್ಕೃತಿಕ ಇತಿಹಾಸೊನು ಒಟ್ಟುಗು ಸೇರ್ಸುನ ತಾಣ ಆಂಡ್.<ref name="ESU"/> ಆಧುನಿಕ ಉಕ್ರೇನ್ ದ ವಿಶ್ವಕೋಶದ ಪ್ರಕಾರ ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್ ಬೊಕ್ಕ ಯುರೋಪ್‌ದ ಎಸ್ಟೇಟ್ ವಾಸ್ತುಶಿಲ್ಪೊದ ಪ್ರಮುಖ ಉದಾಹರಣೆಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. ಉಕ್ರೇನ್‌ಡು ಅರೆನ ಐತಿಹಾಸಿಕ ಸಮಗ್ರತೊನು ಒರಿಪಾದಿನ ಅಪರೂಪದ ಅರಮನೆ-ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಆದುಂಡು.<ref name="ESU"/> 19ನೇ ಶತಮಾನೊಡು ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಆಶ್ರಯೊಡು ಕಚಾನಿವ್ಕಾ ಉಕ್ರೇನ್‌ದ ಬುದ್ಧಿಜೀವಿ, ಸಾಹಿತ್ಯಿಕ ಬೊಕ್ಕ ಕಲಾವಿದರೆನ ಭೇಟಿದ ಪ್ರಮುಖ ಕೇಂದ್ರ ಆದುಂಡು.<ref name="EIU"/> == ಪ್ರವಾಸೋದ್ಯಮ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಇತ್ತೆ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಪ್ರವಾಸೋದ್ಯಮೊದ ಪ್ರಮುಖ ತಾಣ ಆದುಂಡು. ಅರಮನೆ, ವಿಶಾಲ ಪಾರ್ಕ್, ಐತಿಹಾಸಿಕ ಚರ್ಚ್, ಪಾರ್ಕ್‌ದ ರಚನೆಲು ಬೊಕ್ಕ ಟಾರ್ನೋವ್ಸ್ಕಿ ಕುಟುಂಬೊದ ಸಾಂಸ್ಕೃತಿಕ ಪರಂಪರೆ ಪ್ರವಾಸಿಗರೆಗ್ ಪ್ರಮುಖ ಆಕರ್ಷಣೆಲು ಆದುಂಡು.<ref name="OfficialReserve"/> ಅರಮನೆ ಬೊಕ್ಕ ಪಾರ್ಕ್ ಸಮುಚ್ಚಯೊನು ಒಟ್ಟುಗು ಸಂರಕ್ಷಿಸುನ ಕಾರಣೊಡು ಕಚಾನಿವ್ಕಾ ಕೇವಲ ಒಂಜಿ ಕಟ್ಟಡ ಅತ್ತಂದೆ ಒಂಜಿ ಸಮಗ್ರ ಸಾಂಸ್ಕೃತಿಕ ಭೂದೃಶ್ಯ ತಾಣ ಆದುಂಡು. == ಸಂರಕ್ಷಣೆ == ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಮುಖ್ಯ ಉದ್ದೇಶೊ ಅರಮನೆ, ಪಾರ್ಕ್, ಚರ್ಚ್ ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಸಾಂಸ್ಕೃತಿಕ ಬಳಕೆ ಆದುಂಡು.<ref name="President"/> 2001ರ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊಡುಲಾ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊದ ಕಟ್ಟಡೊಲೆನ ಸಂರಕ್ಷಣೆ, ಪುನಃಸ್ಥಾಪನೆ ಬೊಕ್ಕ ಪುನರುಜ್ಜೀವನೊಗು ಹೆಚ್ಚುವರಿ ಕ್ರಮೊಲೆನ್ ಮಲ್ಪುನ ಅಗತ್ಯೊನು ಸೂಚಿಸಾದೆರ್.<ref name="President"/> == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲುಲು == * [https://kachanivka.event.net.ua/site/about ಕಚಾನಿವ್ಕಾ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ಸಾಂಸ್ಕೃತಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * [https://kachanivka.in.ua/ ಕಚಾನಿವ್ಕಾ — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kachanivka National Historical and Cultural Reserve}} [[Category:ಉಕ್ರೇನ್‌ದ ಅರಮನೆಲು]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಕಟ್ಟಡೊಲು ಬೊಕ್ಕ ರಚನೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:1770ರ ದಶಕೊದ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಚೆರ್ನಿಹಿವ್ ಒಬ್ಲಾಸ್ಟ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ನವಶಾಸ್ತ್ರೀಯ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ಐತಿಹಾಸಿಕ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಉದ್ಯಾನೊಲು]] 8u5uz449qmgm29jnc6vzjh41er23hpc ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ (ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ) 0 28041 366155 2026-08-21T08:23:45Z Mahaveer Indra 1023 ಪೊಸ ಬರವು 366155 wikitext text/x-wiki '''ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ''' ನಮ ದೇಸೊದ [[ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಲು|ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ]]ಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. ಕಾಶ್ಮೀರ ಇಬಾಗೊದ ಬಡೆಕಾಯಿದ ಮಲ್ಲ ಬಾಗ ಈ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಡು ಉಂಡು.<ref name=britannica-2022-j&k>{{citation|last1=Akhtar|first1=Rais|last2=Kirk|first2=William|chapter=Jammu and Kashmir|title=Encyclopaedia Britannica|date=22 March 2021|publisher=Encyclopædia Britannica, Inc.|chapter-url=https://www.britannica.com/place/Jammu-and-Kashmir|access-date=2 April 2022|quote=The union territory is part of the larger region of Kashmir, which has been the subject of dispute between India, Pakistan, and China since the partition of the subcontinent in 1947. ... The territory that India administered on its side of the line, which contained both Jammu (the seat of the Dogra dynasty) and the Vale of Kashmir, took on the name Jammu and Kashmir. However, both India and Pakistan have continued to claim the entire Kashmir region|archive-date=19 June 2015|archive-url=https://web.archive.org/web/20150619220652/https://www.britannica.com/place/Jammu-and-Kashmir|url-status=live}}</ref> ಮುಲ್ಪ ಇಪ್ಪುನ ಗಡಿ ನಿಯಂತ್ರಣ ರೇಖೆದ ಒಂಜಿ ಬರಿಟ್ ಪಾಕಿಸ್ತಾನ ಆಕ್ರಮಿಸಾದಿನ ಪಾಕ್ ಆಕ್ರಮಿತ ಕಾಶ್ಮೀರ ಬೊಕ್ಕ ಗಿಲ್ಗಿಟ್-ಬಾಲ್ಟಿಸ್ತಾನ್ ಇತ್ತ್ಂಡ ನನೊಂಜಿ ಬರಿಟ್ ನಮ ದೇಸೊದ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ ಆಯಿನ ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ ಉಂಡು. ಭಾರತದ ರಾಜ್ಯೊಲಾಯಿನ ಹಿಮಾಚಲ ಪ್ರದೇಶ ಬೊಕ್ಕ ಪಂಜಾಬ್‌ದ ಬಡಕಾಯಿಡ್ ಬೊಕ್ಕ ಭಾರತದ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶವಾಯಿನ ಲಡಾಖ್‌ದ ಪಡ್ಡಾಯಿಡ್ ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ ಉಂಡು. ೨೦೧೯ಡ್ ಜಮ್ಮು-ಕಾಶ್ಮೀರ ಪುನರ್ವಿಂಗಡಣಾ ಕಾಯ್ದೆನ್ ಜಾರಿಗ್ ಕನತ್‌ದ್, ಪಿರಾಕ್‌ದ ಜಮ್ಮು-ಕಾಶ್ಮೀರ ರಾಜ್ಯೊನು ರಡ್ಡ್ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶೊಲಾದ್ ವಿಂಗಡಣೆ ಮಲ್ತೆರ್. ಅವು ಪಂಡ ಮೂಡಾಯಿಡ್ ಲಡಾಖ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿಡ್ ಒರಿದಿನ ಜಮ್ಮು-ಕಾಶ್ಮೀರ. ಶ್ರೀನಗರ ಬೊಕ್ಕ ಜಮ್ಮು ಒಟ್ಟಾದ್ ಈ ಪ್ರದೇಶದ ರಾಜಧಾನಿಯಾದ್ ಕೆಲಸ ಮಲ್ಪುಂಡು; ಉಂದು ರಡ್ಡ್ ವಿಭಾಗ ಬೊಕ್ಕ ೨೦ ಜಿಲ್ಲೆಲಾದ್ ವಿಂಗಡಣೆ ಆತ್ಂಡ್. ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರೊಡು ನೀಲಮಣಿ, ಬೊರಾಕ್ಸ್ ಬೊಕ್ಕ ಗ್ರ್ಯಾಫೈಟ್‌ದ ಮಲ್ಲ ಖನಿಜ ನಿಕ್ಷೇಪೊಲು ಉಂಡು. ಕೃಷಿ ಬೊಕ್ಕ ಸೇವಾ ವಲಯಲು ಮುಲ್ಪದ ಆರ್ಥಿಕತೆದ ಕಂಬ ಆದುಂಡು. ತೋಟಗಾರಿಕೆ, ಕರಕುಶಲ ವಸ್ತುಲು ಬೊಕ್ಕ ಪ್ರವಾಸೋದ್ಯಮ ನೆಕ್ಕ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರ್ಪುಂಡು. ಸೇಬು ಕೃಷಿ ಮುಲ್ಪದ ಮಲ್ಲ ಉದ್ಯಮೊಲೆಡ್ ಒಂಜಿ. ಕಾಶ್ಮೀರದ ಸೇಬು ದೇಸೊರ್ಮೆ ಪುಗರ್ತೆ ಪಡೆದಿನ ಒಂಜಿ ಪರ್ಂದ್. ಉಂದು ೩೫ ಲಕ್ಷ ಜನಕ್ಲೆಗ್ ಬೇಲೆ ಕೊರ್ಪುಂಡು. ರಾಜ್ಯದ ಒಟ್ಟು ಆಂತರಿಕ ಉತ್ಪನ್ನೊಡು ೧೦% ಪಾಲ್ ಸೇಬು ಬುಲೆಟ್ ಬರ್ಪುಂಡು. ==ಇತಿಹಾಸೊ== ==ಹವಾಗುಣ == ==ಜನಸಂಕ್ಯಾ ಶಾಸ್ತ್ರೊ== ==ಸರಕಾರ== ==ಆರ್ತಿಕತೆ== == ಉಲ್ಲೇಕ== mt2cq3u6rroseym66aoawu5ggci7dr8 366156 366155 2026-08-21T08:25:36Z Mahaveer Indra 1023 366156 wikitext text/x-wiki '''ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ''' ನಮ ದೇಸೊದ [[ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಲು|ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ]]ಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. ಕಾಶ್ಮೀರ ಇಬಾಗೊದ ಬಡೆಕಾಯಿದ ಮಲ್ಲ ಬಾಗ ಈ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಡು ಉಂಡು.<ref name=britannica-2022-j&k>{{citation|last1=Akhtar|first1=Rais|last2=Kirk|first2=William|chapter=Jammu and Kashmir|title=Encyclopaedia Britannica|date=22 March 2021|publisher=Encyclopædia Britannica, Inc.|chapter-url=https://www.britannica.com/place/Jammu-and-Kashmir|access-date=2 April 2022|quote=The union territory is part of the larger region of Kashmir, which has been the subject of dispute between India, Pakistan, and China since the partition of the subcontinent in 1947. ... The territory that India administered on its side of the line, which contained both Jammu (the seat of the Dogra dynasty) and the Vale of Kashmir, took on the name Jammu and Kashmir. However, both India and Pakistan have continued to claim the entire Kashmir region|archive-date=19 June 2015|archive-url=https://web.archive.org/web/20150619220652/https://www.britannica.com/place/Jammu-and-Kashmir|url-status=live}}</ref> ಮುಲ್ಪ ಇಪ್ಪುನ ಗಡಿ ನಿಯಂತ್ರಣ ರೇಖೆದ ಒಂಜಿ ಬರಿಟ್ ಪಾಕಿಸ್ತಾನ ಆಕ್ರಮಿಸಾದಿನ ಪಾಕ್ ಆಕ್ರಮಿತ ಕಾಶ್ಮೀರ ಬೊಕ್ಕ ಗಿಲ್ಗಿಟ್-ಬಾಲ್ಟಿಸ್ತಾನ್ ಇತ್ತ್ಂಡ ನನೊಂಜಿ ಬರಿಟ್ ನಮ ದೇಸೊದ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ ಆಯಿನ ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ ಉಂಡು. ಭಾರತದ ರಾಜ್ಯೊಲಾಯಿನ ಹಿಮಾಚಲ ಪ್ರದೇಶ ಬೊಕ್ಕ ಪಂಜಾಬ್‌ದ ಬಡಕಾಯಿಡ್ ಬೊಕ್ಕ ಭಾರತದ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶವಾಯಿನ ಲಡಾಖ್‌ದ ಪಡ್ಡಾಯಿಡ್ ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ ಉಂಡು. ೨೦೧೯ಡ್ ಜಮ್ಮು-ಕಾಶ್ಮೀರ ಪುನರ್ವಿಂಗಡಣಾ ಕಾಯ್ದೆನ್ ಜಾರಿಗ್ ಕನತ್‌ದ್, ಪಿರಾಕ್‌ದ ಜಮ್ಮು-ಕಾಶ್ಮೀರ ರಾಜ್ಯೊನು ರಡ್ಡ್ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶೊಲಾದ್ ವಿಂಗಡಣೆ ಮಲ್ತೆರ್. ಅವು ಪಂಡ ಮೂಡಾಯಿಡ್ ಲಡಾಖ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿಡ್ ಒರಿದಿನ ಜಮ್ಮು-ಕಾಶ್ಮೀರ. ಶ್ರೀನಗರ ಬೊಕ್ಕ ಜಮ್ಮು ಒಟ್ಟಾದ್ ಈ ಪ್ರದೇಶದ ರಾಜಧಾನಿಯಾದ್ ಕೆಲಸ ಮಲ್ಪುಂಡು; ಉಂದು ರಡ್ಡ್ ವಿಭಾಗ ಬೊಕ್ಕ ೨೦ ಜಿಲ್ಲೆಲಾದ್ ವಿಂಗಡಣೆ ಆತ್ಂಡ್.<ref name=ladakh-britannica-current>{{citation|chapter=Ladakh|title=Encyclopaedia Britannica|date=1 March 2021|publisher=Encyclopædia Britannica|chapter-url=https://www.britannica.com/place/Ladakh|access-date=2 April 2022|quote=Ladakh, large area of the northern and eastern Kashmir region, northwestern Indian subcontinent. Administratively, Ladakh is divided between Pakistan (northwest), as part of Gilgit-Baltistan, and India (southeast), as part of Ladakh union territory (until October 31, 2019, part of Jammu and Kashmir state); in addition, China administers portions of northeastern Ladakh.|archive-date=7 April 2022|archive-url=https://web.archive.org/web/20220407153837/https://www.britannica.com/place/Ladakh|url-status=live}}</ref> ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರೊಡು ನೀಲಮಣಿ, ಬೊರಾಕ್ಸ್ ಬೊಕ್ಕ ಗ್ರ್ಯಾಫೈಟ್‌ದ ಮಲ್ಲ ಖನಿಜ ನಿಕ್ಷೇಪೊಲು ಉಂಡು. ಕೃಷಿ ಬೊಕ್ಕ ಸೇವಾ ವಲಯಲು ಮುಲ್ಪದ ಆರ್ಥಿಕತೆದ ಕಂಬ ಆದುಂಡು. ತೋಟಗಾರಿಕೆ, ಕರಕುಶಲ ವಸ್ತುಲು ಬೊಕ್ಕ ಪ್ರವಾಸೋದ್ಯಮ ನೆಕ್ಕ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರ್ಪುಂಡು. ಸೇಬು ಕೃಷಿ ಮುಲ್ಪದ ಮಲ್ಲ ಉದ್ಯಮೊಲೆಡ್ ಒಂಜಿ. ಕಾಶ್ಮೀರದ ಸೇಬು ದೇಸೊರ್ಮೆ ಪುಗರ್ತೆ ಪಡೆದಿನ ಒಂಜಿ ಪರ್ಂದ್. ಉಂದು ೩೫ ಲಕ್ಷ ಜನಕ್ಲೆಗ್ ಬೇಲೆ ಕೊರ್ಪುಂಡು. ರಾಜ್ಯದ ಒಟ್ಟು ಆಂತರಿಕ ಉತ್ಪನ್ನೊಡು ೧೦% ಪಾಲ್ ಸೇಬು ಬುಲೆಟ್ ಬರ್ಪುಂಡು. ==ಇತಿಹಾಸೊ== ==ಹವಾಗುಣ == ==ಜನಸಂಕ್ಯಾ ಶಾಸ್ತ್ರೊ== ==ಸರಕಾರ== ==ಆರ್ತಿಕತೆ== == ಉಲ್ಲೇಕ== limhag96zmgkjy1youc3zj662pd3lqw 366167 366156 2026-08-21T09:38:27Z Mahaveer Indra 1023 366167 wikitext text/x-wiki {{under construction}} '''ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ''' ನಮ ದೇಸೊದ [[ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಲು|ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ]]ಲೆಡ್ ಒಂಜಿ ಆದುಂಡು. ಕಾಶ್ಮೀರ ಇಬಾಗೊದ ಬಡೆಕಾಯಿದ ಮಲ್ಲ ಬಾಗ ಈ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊಡು ಉಂಡು.<ref name=britannica-2022-j&k>{{citation|last1=Akhtar|first1=Rais|last2=Kirk|first2=William|chapter=Jammu and Kashmir|title=Encyclopaedia Britannica|date=22 March 2021|publisher=Encyclopædia Britannica, Inc.|chapter-url=https://www.britannica.com/place/Jammu-and-Kashmir|access-date=2 April 2022|quote=The union territory is part of the larger region of Kashmir, which has been the subject of dispute between India, Pakistan, and China since the partition of the subcontinent in 1947. ... The territory that India administered on its side of the line, which contained both Jammu (the seat of the Dogra dynasty) and the Vale of Kashmir, took on the name Jammu and Kashmir. However, both India and Pakistan have continued to claim the entire Kashmir region|archive-date=19 June 2015|archive-url=https://web.archive.org/web/20150619220652/https://www.britannica.com/place/Jammu-and-Kashmir|url-status=live}}</ref> ಮುಲ್ಪ ಇಪ್ಪುನ ಗಡಿ ನಿಯಂತ್ರಣ ರೇಖೆದ ಒಂಜಿ ಬರಿಟ್ ಪಾಕಿಸ್ತಾನ ಆಕ್ರಮಿಸಾದಿನ ಪಾಕ್ ಆಕ್ರಮಿತ ಕಾಶ್ಮೀರ ಬೊಕ್ಕ ಗಿಲ್ಗಿಟ್-ಬಾಲ್ಟಿಸ್ತಾನ್ ಇತ್ತ್ಂಡ ನನೊಂಜಿ ಬರಿಟ್ ನಮ ದೇಸೊದ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಸೊ ಆಯಿನ ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ ಉಂಡು. ಭಾರತದ ರಾಜ್ಯೊಲಾಯಿನ ಹಿಮಾಚಲ ಪ್ರದೇಶ ಬೊಕ್ಕ ಪಂಜಾಬ್‌ದ ಬಡಕಾಯಿಡ್ ಬೊಕ್ಕ ಭಾರತದ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶವಾಯಿನ ಲಡಾಖ್‌ದ ಪಡ್ಡಾಯಿಡ್ ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರ ಉಂಡು. ೨೦೧೯ಡ್ ಜಮ್ಮು-ಕಾಶ್ಮೀರ ಪುನರ್ವಿಂಗಡಣಾ ಕಾಯ್ದೆನ್ ಜಾರಿಗ್ ಕನತ್‌ದ್, ಪಿರಾಕ್‌ದ ಜಮ್ಮು-ಕಾಶ್ಮೀರ ರಾಜ್ಯೊನು ರಡ್ಡ್ ಕೇಂದ್ರಾಡಳಿತ ಪ್ರದೇಶೊಲಾದ್ ವಿಂಗಡಣೆ ಮಲ್ತೆರ್. ಅವು ಪಂಡ ಮೂಡಾಯಿಡ್ ಲಡಾಖ್ ಬೊಕ್ಕ ಪಡ್ಡಾಯಿಡ್ ಒರಿದಿನ ಜಮ್ಮು-ಕಾಶ್ಮೀರ. ಶ್ರೀನಗರ ಬೊಕ್ಕ ಜಮ್ಮು ಒಟ್ಟಾದ್ ಈ ಪ್ರದೇಶದ ರಾಜಧಾನಿಯಾದ್ ಕೆಲಸ ಮಲ್ಪುಂಡು; ಉಂದು ರಡ್ಡ್ ವಿಭಾಗ ಬೊಕ್ಕ ೨೦ ಜಿಲ್ಲೆಲಾದ್ ವಿಂಗಡಣೆ ಆತ್ಂಡ್.<ref name=ladakh-britannica-current>{{citation|chapter=Ladakh|title=Encyclopaedia Britannica|date=1 March 2021|publisher=Encyclopædia Britannica|chapter-url=https://www.britannica.com/place/Ladakh|access-date=2 April 2022|quote=Ladakh, large area of the northern and eastern Kashmir region, northwestern Indian subcontinent. Administratively, Ladakh is divided between Pakistan (northwest), as part of Gilgit-Baltistan, and India (southeast), as part of Ladakh union territory (until October 31, 2019, part of Jammu and Kashmir state); in addition, China administers portions of northeastern Ladakh.|archive-date=7 April 2022|archive-url=https://web.archive.org/web/20220407153837/https://www.britannica.com/place/Ladakh|url-status=live}}</ref> ಜಮ್ಮು ಬೊಕ್ಕ ಕಾಶ್ಮೀರೊಡು ನೀಲಮಣಿ, ಬೊರಾಕ್ಸ್ ಬೊಕ್ಕ ಗ್ರ್ಯಾಫೈಟ್‌ದ ಮಲ್ಲ ಖನಿಜ ನಿಕ್ಷೇಪೊಲು ಉಂಡು. ಕೃಷಿ ಬೊಕ್ಕ ಸೇವಾ ವಲಯಲು ಮುಲ್ಪದ ಆರ್ಥಿಕತೆದ ಕಂಬ ಆದುಂಡು. ತೋಟಗಾರಿಕೆ, ಕರಕುಶಲ ವಸ್ತುಲು ಬೊಕ್ಕ ಪ್ರವಾಸೋದ್ಯಮ ನೆಕ್ಕ್ ಮಲ್ಲ ಕೊಡುಗೆ ಕೊರ್ಪುಂಡು. ಸೇಬು ಕೃಷಿ ಮುಲ್ಪದ ಮಲ್ಲ ಉದ್ಯಮೊಲೆಡ್ ಒಂಜಿ. ಕಾಶ್ಮೀರದ ಸೇಬು ದೇಸೊರ್ಮೆ ಪುಗರ್ತೆ ಪಡೆದಿನ ಒಂಜಿ ಪರ್ಂದ್. ಉಂದು ೩೫ ಲಕ್ಷ ಜನಕ್ಲೆಗ್ ಬೇಲೆ ಕೊರ್ಪುಂಡು. ರಾಜ್ಯದ ಒಟ್ಟು ಆಂತರಿಕ ಉತ್ಪನ್ನೊಡು ೧೦% ಪಾಲ್ ಸೇಬು ಬುಲೆಟ್ ಬರ್ಪುಂಡು. ==ಇತಿಹಾಸೊ== ==ಹವಾಗುಣ == ==ಜನಸಂಕ್ಯಾ ಶಾಸ್ತ್ರೊ== ==ಸರಕಾರ== ==ಆರ್ತಿಕತೆ== == ಉಲ್ಲೇಕ== eils8fac9092x4046cmwfp55u6d59vp ಕೀವ್ ಕೋಟೆ 0 28042 366160 2026-08-21T09:09:08Z Hariprasad Shetty10 6127 ಹೊಸ ಪುಟ: {{Infobox | bodyclass = vcard | aboveclass = fn org | above = ಕೀವ್ ಕೋಟೆ | image = Kiev fortress 003 SHCH.jpg | caption = ಕೀವ್ ಕೋಟೆ | label1 = ಸ್ಥಳೀಯ ಹೆಸರು | data1 = Київська фортеця | label2 = ಸ್ಥಳ | data2 = ಹಾಸ್ಪಿಟಲ್ ರಸ್ತೆ 24A, ಕೀವ್, ಉಕ್ರೇನ್ | label3 = ಪ್ರಕಾರ | data3 = ಐತಿಹಾಸಿಕ ಕೋಟೆ ಬೊಕ್ಕ ಕೋಟ... 366160 wikitext text/x-wiki {{Infobox | bodyclass = vcard | aboveclass = fn org | above = ಕೀವ್ ಕೋಟೆ | image = Kiev fortress 003 SHCH.jpg | caption = ಕೀವ್ ಕೋಟೆ | label1 = ಸ್ಥಳೀಯ ಹೆಸರು | data1 = Київська фортеця | label2 = ಸ್ಥಳ | data2 = ಹಾಸ್ಪಿಟಲ್ ರಸ್ತೆ 24A, ಕೀವ್, ಉಕ್ರೇನ್ | label3 = ಪ್ರಕಾರ | data3 = ಐತಿಹಾಸಿಕ ಕೋಟೆ ಬೊಕ್ಕ ಕೋಟೆ ಸಮುಚ್ಚಯ | label4 = ನಿರ್ಮಾಣ ಕಾಲ | data4 = 17ನೇ–19ನೇ ಶತಮಾನೊಲು | label5 = ಸಾರ್ವಜನಿಕ ಸಾರಿಗೆ | data5 = ಕ್ಲೋವ್ಸ್ಕಾ ಮೆಟ್ರೊ ನಿಲ್ದಾಣ; ಪಲಾತ್ಸ್ ಸ್ಪೋರ್ತು ಮೆಟ್ರೊ ನಿಲ್ದಾಣ; ಟ್ರಾಲಿಬಸ್ }} [[File:AFRE Kyev.jpg|thumb|ಕೀವ್ ಕೋಟೆದ 1830ರ ಯೋಜನೆ]] '''ಕೀವ್ ಕೋಟೆ''' ಕೀವ್‌ಡು ಇಪ್ಪುನ ಒಂಜಿ ಐತಿಹಾಸಿಕ ಕೋಟೆ ಸಮುಚ್ಚಯ. ಉಂದು 17ನೇ ಶತಮಾನೊಡ್ದ್ 19ನೇ ಶತಮಾನೊದ ತನಕ ಹಂತ ಹಂತವಾದ್ ನಿರ್ಮಾಣ ಆಯಿನ ಕೋಟೆಲು, ರಕ್ಷಣಾ ಗೋಡೆಲು, ದಂಡೆಲು, ಗೋಪುರೊಲು, ಬ್ಯಾರಕ್‌ಲು ಬೊಕ್ಕ ಬೇತೆ ಸೈನಿಕ ಕಟ್ಟಡೊಲೆನ ಸಮೂಹ ಆದುಂಡು.ಉಂದು ಕೀವ್ ಕೋಟೆದ ಕೆಲವು ಭಾಗೊಲೆನ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ ಭಾಗೊಲೆನಾದ್ ಸಂರಕ್ಷಣೆ ಮಲ್ದೆರ್.<ref name="IEU">{{cite web |title=Kyivan Cave Fortress |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CK%5CY%5CKyivanCaveFortress.htm |website=Internet Encyclopedia of Ukraine |publisher=Canadian Institute of Ukrainian Studies, University of Alberta |access-date=21 August 2026 }}</ref> ಕೋಟೆ ಸಮುಚ್ಚಯೊದ ಮುಖ್ಯ ಭಾಗೊಲು ಕೀವ್‌ದ ಪೆಚೆರ್ಸ್ಕ್ ಪ್ರದೇಶೊಡು ಉಂಡು. ಇಂಚಿನ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ, ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ, ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ, ಲೈಸಾ ಹೊರಾ ಕೋಟೆ ಬೊಕ್ಕ ಬೇತೆ ಪ್ರತ್ಯೇಕ ರಕ್ಷಣಾ ರಚನೆಲು ಸೇರುದುಂಡು.<ref name="IEU"/> ಉಂದು ಕೀವ್ ಕೋಟೆ ಉಕ್ರೇನ್‌ದ ಪ್ರಮುಖ ಐತಿಹಾಸಿಕ ಸೈನಿಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ತಾಣೊಲೆಡ್ ಒಂಜಿ. ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಪ್ರವಾಸೋದ್ಯಮ ಮೂಲೊದ ಪ್ರಕಾರ, ಕೋಟೆದ ರಚನೆಲು ಸುಮಾರು 200 ವರ್ಷೊಲೆನ ಅವಧಿಡ್, 17ನೇ ಶತಮಾನೊಡ್ದ್ 19ನೇ ಶತಮಾನೊ ಮುಟ್ಟ ನಿರ್ಮಾಣ ಆತುಂಡ್.<ref name="KyivGuide">{{cite web |title=Kyiv Fortress |url=https://guide.kyivcity.gov.ua/en/places/kyyivska-fortetsya |website=Kyiv City Guide |publisher=Kyiv City Council |access-date=21 August 2026 }}</ref> == ಇತಿಹಾಸ == === ಆರಂಭಿಕ ಕೋಟೆ ವ್ಯವಸ್ಥೆ === ಕೀವ್ ನಗರೊಗು ಪುರಾತನ ಕಾಲೊಡ್ದೇ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆ ಇತ್ತ್ಂಡ್. ಕೀವನ್ ರಸ್ ಕಾಲೊಡು ನಗರದ ಮೇಲ್ಭಾಗೊಡು ಕೋಟೆಗೋಡೆಲು, ಮಣ್ಣಿನ ದಂಡೆಲು ಬೊಕ್ಕ ಕಂದಕೊಲು ನಿರ್ಮಾಣ ಆಯಿತ್ತ್ಂಡ್. 1240ಡ್ ಬತು ಖಾನ್ ನೇತೃತ್ವೊದ ಮಂಗೋಲ್ ಸೈನ್ಯ ಕೀವ್‌ನು ಮುತ್ತಿಗೆ ಹಾಕಿ ನಗರೊನು ನಾಶ ಮಲ್ತಿನ ಪಿರವುಟು ನಗರದ ಪರತ್ತ್ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆಗ್ ದೊಡ್ಡ ಹಾನಿ ಆಯಿನಿ.<ref name="KyivHistory">{{cite web |title=Kyiv |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CK%5CY%5CKyiv.htm |website=Internet Encyclopedia of Ukraine |publisher=Canadian Institute of Ukrainian Studies, University of Alberta |access-date=21 August 2026 }}</ref> ಪಿರಾವುಡು ಕೀವ್ ಲಿಥುವೇನಿಯನ್ ಬೊಕ್ಕ ಪೋಲಿಷ್-ಲಿಥುವೇನಿಯನ್ ಆಡಳಿತೊದ ಅಡಿಡ್ ಇತ್ತ್ಂಡ್. 17ನೇ ಶತಮಾನೊಡು ಕೀವ್‌ದ ರಾಜಕೀಯ ಬೊಕ್ಕ ಸೈನಿಕ ಮಹತ್ವೊ ಪಿರಾವುಡು ಹೆಚ್ಚಾಂಡ್.<ref name="KyivHistory"/> === 1654ರ ಪಿರ === 1654ರ ಪೆರಿಯಸ್ಲಾವ್ ಒಪ್ಪಂದೊದ ಪಿರಾವುಡು ಕೀವ್‌ಡು ಮಸ್ಕೋವೈಟ್ ಸೈನಿಕ ಪಡೆದ ಶಿಬಿರೊ ಸ್ಥಾಪನೆ ಆದುಂಡು. ಈ ಕಾಲೊಡು ನಗರದ ಮೇಲ್ಭಾಗೊಡು ಒಂಜಿ ಪೊಸ ಸೈನಿಕ ಕೋಟೆ ನಿರ್ಮಾಣ ಆತುಂಡು.<ref name="KyivHistory"/> ಕೀವ್‌ಡು ಮಸ್ಕೋವೈಟ್ ಸೈನಿಕ ಉಪಸ್ಥಿತಿ ಹೆಚ್ಚಾಯಿನ ಜೊತೆಡು ಪೆಚೆರ್ಸ್ಕ್ ಪ್ರದೇಶೊದ ಸೈನಿಕ ಮಹತ್ವೊಲಾ ಹೆಚ್ಚಾಂಡ್. ಕುಡ ಈ ಪ್ರದೇಶೊಡು ಮಲ್ಲ ಕೋಟೆ ವ್ಯವಸ್ಥೆ ಅಭಿವೃದ್ಧಿ ಆಂಡ್.<ref name="RussianPresence">{{cite web |title=Russians in Ukraine |url=https://www.encyclopediaofukraine.com/display.asp?linkpath=pages%5CR%5CU%5CRussiansinUkraine.htm |website=Internet Encyclopedia of Ukraine |publisher=Canadian Institute of Ukrainian Studies, University of Alberta |access-date=21 August 2026 }}</ref> === 1679ರ ಕೋಟೆ ನಿರ್ಮಾಣ === 1679ಡ್ ಹೆಟ್ಮನ್ ಇವಾನ್ ಸಮೊಯ್ಲೊವಿಚ್ ಕಾಲೊಡು ಮೇಲಿನ ಮಠ ಪ್ರದೇಶೊದ ಸುತ್ತ ಪೊಸ ಕಣಿಕುಲು ಬೊಕ್ಕ ಮಣ್ಣಿನ ದಂಡೆಲು ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಈ ಕೆಲಸೊ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಅಭಿವೃದ್ಧಿಗ್ ಒಂಜಿ ಮುಖ್ಯ ಹಂತ ಆಂಡ್.<ref name="IEU"/> ಪಿರಾವುಡು ಹೆಟ್ಮನ್ ಇವಾನ್ ಮಜೆಪಾ ಕಾಲೊಡು ದಪ್ಪ ಕಲ್ಲಿನ ಗೋಡೆಲು ಬೊಕ್ಕ ನಾಲ್ಕು ಗೋಪುರೊಲು ಸೇರ್ಸಾಯಿನಿ. ಈ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆ ಕೀವ್ ಪೆಚೆರ್ಸ್ಕ್ ಪ್ರದೇಶೊನು ಹೆಚ್ಚು ಬಲಪಡಿಸಾಯಿನಿ.<ref name="IEU"/> == ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ದ ಕೋಟೆ == 1706ಡ್ ರಷ್ಯಾದ ಚಕ್ರವರ್ತಿ ಪೀಟರ್ ಪ್ರಥಮ ಕೀವ್‌ಡು ಹೊಸ ಸೈನಿಕ ಕೋಟೆ ನಿರ್ಮಾಣ ಮಲ್ಪುನ ಆದೇಶ ಕೊರಿಯೆರ್. ಈ ಕೋಟೆನು '''ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ''' ಅತ್ತ್ಂಡ '''ಓಲ್ಡ್ ಕೇವ್ ಕೋಟೆ''' ಪಂಡ್ದ್ ಲೆಪ್ಪುವೆರ್.<ref name="IEU"/> ಕೋಟೆದ ಮೊದಲ ಯೋಜನೆ ಎಂಜಿನಿಯರ್ ಗೆಲ್ಲರ್ಟ್ ತಯಾರ್ ಮಲ್ತೆರ್. 1723ಡ್ ಕೋಟೆದ ಮುಖ್ಯ ನಿರ್ಮಾಣ ಪೂರ್ಣ ಆಂಡ್. ಉಂದು ಅರ್ಧವೃತ್ತಾಕಾರೊದ ಸಿಟಾಡೆಲ್, ಸುಮಾರು 6 ಮೀಟರ್ ಎತ್ತರದ ಮಣ್ಣಿನ ದಂಡೆಲು, ಎಂಟು ಬಲವರ್ಧಿತ ಭಾಗೊಲು ಬೊಕ್ಕ ಬೇತೆ ರಕ್ಷಣಾ ರಚನೆಲೆನ್ ಹೊಂದಿತ್ತ್ಂಡ್.<ref name="IEU"/> ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಮೂಲ ಮೂಜಿ ಮುಖ್ಯ ಬಾಗಿಲೊಲೆಡ್ ವಾಸಿಲ್ಕಿವ್ ಬೊಕ್ಕ ಮಾಸ್ಕೋ ಬಾಕಿಲುಲು ಪಿರಾವುಡು ಒರಿದಿತ್ತ್ಂಡ್. ಕೋಟೆ ರಷ್ಯನ್ ಸಾಮ್ರಾಜ್ಯೊದ ಕೀವ್‌ದ ಸೈನಿಕ ಬೊಕ್ಕ ಆಡಳಿತಾತ್ಮಕ ಕೇಂದ್ರೊಲೆಡ್ ಒಂಜಿ ಆತ ಬಳಕೆ ಆಯಿನಿ.<ref name="IEU"/> [[File:Kyiv Arsenal panorama.jpg|thumb|ಕೀವ್ ಆರ್ಸೆನಲ್]] == ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ == 1831ಡ್ದ್ 1863ರ ತನಕ್ ಕೀವ್‌ಡು '''ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ''' ನಿರ್ಮಾಣ ಆಯಿನಿ. ಈ ಕೋಟೆದ ನಿರ್ಮಾಣೊ ಎಂಜಿನಿಯರ್ ಒಪ್ಪರ್ಮನ್‌ದ ಮಾರ್ಗದರ್ಶನೊಡು ನಡತ್ಂಡ್.<ref name="IEU"/> ಪೊಸ ಕೋಟೆ ವ್ಯವಸ್ಥೆಡು ಪೊಸ ಮಣ್ಣುದ ದಂಡೆಲು, ಬ್ಯಾರಕ್‌ಲು, ರಕ್ಷಣಾ ಗೋಡೆಲು ಬೊಕ್ಕ ಬೇತೆ ಸೈನಿಕ ಕಟ್ಟಡೊಲು ನಿರ್ಮಾಣ ಆಂಡ್. ಕೆಲವು ಪೊಸ ದಂಡೆಲು 15 ಮೀಟರ್ ತನಕ ಎತ್ತರ ಇತ್ತ್ಂಡ್ ಬೊಕ್ಕ ಒಂಜಿ ಪ್ರಮುಖ ದಂಡೆದ ಉದ್ದ ಸುಮಾರು 1,039 ಮೀಟರ್ ಇತ್ತ್ಂಡ್.<ref name="IEU"/> ಪೊಸ ಕೋಟೆದ ಮುಖ್ಯ ಭಾಗೊಲೆಡ್ '''ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ''' ಬೊಕ್ಕ '''ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ''' ಪ್ರಮುಖ ಆದುಂಡು. ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆಡು ಸೈನಿಕ ಆಸ್ಪತ್ರೆಲಾ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್.<ref name="IEU"/> == ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ == '''ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ''' (Hospital Fortification) ಕೀವ್ ಕೋಟೆದ ಪ್ರಮುಖ ಭಾಗೊಲೆಡ್ ಒಂಜಿ. ಉಂದು ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ನಿರ್ಮಾಣೊದ ಕಾಲೊಡು ಅಭಿವೃದ್ಧಿ ಆತುಂಡು. ಇಂಚ ಸೈನಿಕ ಆಸ್ಪತ್ರೆ ಬೊಕ್ಕ ರಕ್ಷಣಾ ಕಟ್ಟಡೊಲು ಇತ್ತ್ಂಡ್.<ref name="IEU"/> ಉಂದು ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಪ್ರದೇಶೊಡು ಕೋಟೆಗೋಡೆ, ರಕ್ಷಣಾ ದಂಡೆಲು, ಕ್ಯಾಪೊನಿಯರ್‌ಲು ಬೊಕ್ಕ ಬೇತೆ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಉಳಿದ್ಂಡ್. ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ''ಕೀವ್ ಕೋಟೆ''ದ ಪ್ರಮುಖ ಭಾಗೊ ಇಂಚೆಡ್ ಕಾರ್ಯ ನಿರ್ವಹಿಸುಂಡು.<ref name="KyivMuseum">{{cite web |title=Національний історико-архітектурний музей «Київська фортеця» |url=https://dk.kyivcity.gov.ua/muzei/natsionalnyi-istoryko-arkhitekturnyi-muzei-kyivska-fortetsia |website=Department of Culture, Kyiv City Council |date=24 April 2024 |access-date=21 August 2026 }}</ref> [[File:Северная полубашня Госпитального замка.JPG|thumb|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಉತ್ತರ ಅರೆ-ಗೋಪುರ]] == ಕ್ಯಾಪೊನಿಯರ್ == ಕೀವ್ ಕೋಟೆದ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆಡು ಕ್ಯಾಪೊನಿಯರ್‌ಲು ಪ್ರಮುಖ ರಚನೆಲು ಆತುಂಡು. ಕ್ಯಾಪೊನಿಯರ್ ಪಂಡ ಕೋಟೆದ ಕಂದಕ ಬೊಕ್ಕ ಗೋಡೆದ ರಕ್ಷಣೆದ ಬಳಸುನ ಸೈನಿಕ ರಚನೆ. '''ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್''' (Kosyi Kaponir) ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಒಂಜಿ ಪ್ರಸಿದ್ಧ ರಕ್ಷಣಾ ರಚನೆ. ಉಂದು ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ ಭಾಗ ಆದುಂಡು. ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಮೂಲೊದ ಪ್ರಕಾರ, ಕೀವ್ ಕೋಟೆದ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಸಮುಚ್ಚಯೊಡು ರಕ್ಷಣಾ ದಂಡೆಲು, ಕೋಟೆಗೋಡೆ ಬೊಕ್ಕ ಕ್ಯಾಪೊನಿಯರ್‌ಲೆನ ಬಯಲು ಪ್ರದರ್ಶನೊ ಉಂಡು.<ref name="KyivGuide"/> [[File:Kiev-FortificationKosoyKaponir 05.jpg|thumb|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಕ್ಯಾಪೊನಿಯರ್‌ಡು ಫಿರಂಗಿಲು]] == ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ == '''ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ''' ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಒಂಜಿ ಮುಖ್ಯ ಭಾಗ. ಉಂದು ವಾಸಿಲ್ಕಿವ್ ದಿಕ್ಕ್‌ದ್ ಕೀವ್‌ಗು ಬರ್ಪುನ ಮಾರ್ಗೊನು ರಕ್ಷಣೆ ಮಲ್ಪುನ ಉದ್ದೇಶೊಡು ನಿರ್ಮಾಣ ಮಲ್ದಿನಿ.<ref name="IEU"/> ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆದ ಒರಿದಿನ ರಚನೆಲೆಡ್ ಕೆಲವು ಗೋಪುರೊಲು ಪ್ರಮುಖ ಆದುಂಡು. ಅವುಲೆಡ್ ಗೋಪುರ ಸಂಖ್ಯೆ 1ರ ರೆಡ್ಯೂಯಿಟ್, ವೃತ್ತಾಕಾರ ಗೋಪುರ ಸಂಖ್ಯೆ 2 ಬೊಕ್ಕ ಪ್ರೊಜೊರಿವ್ಸ್ಕಾ ಗೋಪುರ ಸಂಖ್ಯೆ 3 ಸೇರ್ದ್ ಉಂಡು. == ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ == '''ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ''' ಕೀವ್‌ದ ಜ್ವಿರಿನೆಟ್ಸ್ ಪ್ರದೇಶೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಬೊಕ್ಕೊಂಜಿ ರಕ್ಷಣಾ ಸಮುಚ್ಚಯ ಆಂಡ್. ಉಂದು 19ನೇ ಶತಮಾನೊದ ಕೀವ್ ಕೋಟೆದ ವಿಸ್ತೃತ ರಕ್ಷಣಾ ವ್ಯವಸ್ಥೆದ ಭಾಗ ಆದ್ ಬೆಳೆದುಂಡು. ಜ್ವಿರಿನೆಟ್ಸ್ ಪ್ರದೇಶೊಡು ಪಿರಾವುಡು ನಿರ್ಮಾಣ ಆಯಿನ ಸೈನಿಕ ರಚನೆಲೆನ ಕೆಲವು ಅವಶೇಷೊಲು ಇಂಚ ತೋಜುದು ಬರ್ಪುಂಡು. == ಲೈಸಾ ಹೊರಾ ಕೋಟೆ == '''ಲೈಸಾ ಹೊರಾ''' ("ಬೊಳು ಗುಡ್ಡೆ") ಕೀವ್‌ದ ದಕ್ಷಿಣ ಭಾಗೊಡು ಇಪ್ಪುನ ಐತಿಹಾಸಿಕ ಕೋಟೆ ಪ್ರದೇಶ ಆಂಡ್. 19ನೇ ಶತಮಾನೊದ ಕೊನೆ ಭಾಗೊಡು ಇಂಚ ಕೋಟೆ ರಚನೆಲು ನಿರ್ಮಾಣ ಆಯಿನಾವುಲು. ಪಿರವುಡು ಲೈಸಾ ಪಿದಯಿದ ರಾಜಕೀಯ ಕೈದಿಲೆನ ಮರಣದಂಡನೆದ ಜಾಗೆ ಆತ್ ಬಳಕೆ ಆತುಂಡು. ಉಂದು ಈ ಪ್ರದೇಶೊನು ಭೂದೃಶ್ಯ ಸಂರಕ್ಷಿತ ಪ್ರದೇಶೊ ಬೊಕ್ಕ ಐತಿಹಾಸಿಕ ತಾಣೊ ಆದ್ ಪರಿಗಣಿಸಾವುದುಂಡು. == ಕೋಟೆದ ರಚನೆ == ಕೀವ್ ಕೋಟೆ ಒಂಜಿ ಏಕೈಕ ಕಟ್ಟಡ ಅತ್ತು. ಉಂದು ಮಸ್ತ್ ಪ್ರತ್ಯೇಕ ರಕ್ಷಣಾ ಸಮುಚ್ಚಯೊಲೆನ ಒಟ್ಟುಗೂಡಿಕೆ ಆತುಂಡು. ಮುಖ್ಯ ಭಾಗೊಲೆಡ್: * ಹಳೆ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ * ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆ * ಸಿಟಾಡೆಲ್ * ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆ * ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ * ಜ್ವಿರಿನೆಟ್ಸ್ ಕೋಟೆ * ಲೈಸಾ ಹೊರಾ ಕೋಟೆ * ಕ್ಯಾಪೊನಿಯರ್‌ಲು * ಕೋಟೆಗೋಡೆ ಬೊಕ್ಕ ಮಣ್ಣಿನ ದಂಡೆಲು * ಸೈನಿಕ ಬ್ಯಾರಕ್‌ಲು * ಗೋಪುರೊಲು ಪೊಸ ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಉಳಿದಿನ ಪ್ರಮುಖ ರಚನೆಲೆಡ್ ಮಿಕಿಲ್ಸ್ಕಾ ಬಾಕಿಲು, ವೃತ್ತಾಕಾರ ಗೋಪುರ ಬೊಕ್ಕ ಪ್ರೊಜೊರಿವ್ಸ್ಕಾ ಗೋಪುರ ಸೇರ್ದ್ ಉಂಡು.<ref name="IEU"/> == ಸೈನಿಕ ಮಹತ್ವ == 18ನೇ ಬೊಕ್ಕ 19ನೇ ಶತಮಾನೊಡು ಕೀವ್ ಕೋಟೆ ರಷ್ಯನ್ ಸಾಮ್ರಾಜ್ಯೊದ ಪ್ರಮುಖ ಸೈನಿಕ ಬೊಕ್ಕ ಆಡಳಿತಾತ್ಮಕ ಕೇಂದ್ರೊಲೆಡ್ ಒಂಜಿ ಆಂಡ್. ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಅಭಿವೃದ್ಧಿ ಕೀವ್‌ಡು ರಷ್ಯನ್ ಸೈನಿಕ ಉಪಸ್ಥಿತಿ ಹೆಚ್ಚಾಯಿನ ಜೊತೆಡು ನಡತ್ಂಡ್.<ref name="RussianPresence"/> 19ನೇ ಶತಮಾನೊಡು ಆಧುನಿಕ ತೋಪು ಬೊಕ್ಕ ಪೊಸ ಸೈನಿಕ ತಂತ್ರಜ್ಞಾನೊಲೆನ ಅಭಿವೃದ್ಧಿ ಆಯಿನ ಕಾರಣೊಡು ಪುರಾತನ ಕೋಟೆದ ನೇರ ಸೈನಿಕ ಮಹತ್ವೊ ಕಡಿಮೆ ಆತುಂಡು. ಪಿರಾವುಡು ಕೋಟೆದ ಮಸ್ತ್ ಕಟ್ಟಡೊಲೆನ್ ಸೈನಿಕ ಗೋದಾಮು, ಬ್ಯಾರಕ್ ಬೊಕ್ಕ ಜೈಲು ಆದ್ ಬಳಕೆ ಮಲ್ತೆರ್.<ref name="IEU"/> == 20ನೇ ಶತಮಾನ == 20ನೇ ಶತಮಾನೊಡು ಕೀವ್ ಕೋಟೆದ ಕೆಲವು ಕಟ್ಟಡೊಲು ಸೈನಿಕ, ಆಡಳಿತಾತ್ಮಕ, ಗೋದಾಮು ಬೊಕ್ಕ ಜೈಲು ಉದ್ದೇಶೊಲೆಗ್ ಬಳಕೆ ಆದುಂಡು. ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್ ರಾಜಕೀಯ ಕೈದಿಲೆನ ಜೈಲು ಆದ್ ಬಳಕೆ ಆತುಂಡು. ಪಿರವುಡು ಕೋಟೆದ ಐತಿಹಾಸಿಕ ಬೊಕ್ಕ ವಾಸ್ತುಶಿಲ್ಪೀಯ ಮಹತ್ವೊದ ಗುರುತಿಸಾಯಿನ ಸಂರಕ್ಷಣೆಗ್ ಒತ್ತು ಕೊರಿಯೆರ್. 1930ಡ್ ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್ ಮ್ಯೂಸಿಯಂ ಸ್ಥಾಪನೆ ಆತಿನಿ. 1991ಡ್ ಉಂದು ''ಕೀವ್ ಕೋಟೆ'' ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ಸ್ಮಾರಕ-ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಆತ್ ಮರುನಾಮಕರಣ ಆಂಡ್. 2007ಡ್ ಉಕ್ರೇನ್‌ದ ಅಧ್ಯಕ್ಷರ ಆದೇಶೊದ ಮೂಲಕ ಮ್ಯೂಸಿಯಂ‌ಗು ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="KyivMuseum"/> == ಕೀವ್ ಕೋಟೆದ ವಸ್ತುಸಂಗ್ರಹಾಲಯ == '''ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯ "ಕೀವ್ ಕೋಟೆ"''' ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಆಧಾರೊಡು ನಿರ್ಮಾಣ ಆಯಿನ ಒಂಜಿ ವಸ್ತುಸಂಗ್ರಹಾಲಯ ಸಮುಚ್ಚಯ ಆಂಡ್. ಇಂಚ ಕೀವ್‌ದ ಕೋಟೆ ನಿರ್ಮಾಣೊ ಬೊಕ್ಕ ಉಕ್ರೇನ್‌ದ ಸೈನಿಕ ವಾಸ್ತುಶಿಲ್ಪೊದ ಇತಿಹಾಸೊನು ತೋಜಾವುನ ಪ್ರದರ್ಶನೊಲು ಉಂಡು.<ref name="KyivMuseum"/> ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಮೂಲೊದ ಪ್ರಕಾರ, ಮ್ಯೂಸಿಯಂ ಸಮುಚ್ಚಯೊಡು ಕೀವ್ ಕೋಟೆದ ಕಟ್ಟಡೊಲೆನ ಬಳಕೆದ ಇತಿಹಾಸೊ, ಕೋಟೆ ನಿರ್ಮಾಣೊದ ಇತಿಹಾಸೊ, ತೋಪು ಬೊಕ್ಕ ರಕ್ಷಣಾ ತಂತ್ರಜ್ಞಾನೊ, ಬೊಕ್ಕ ಬಯಲು ಪ್ರದರ್ಶನೊ ಸೇರ್ದ್ ಉಂಡು.<ref name="KyivGuide"/> ಮ್ಯೂಸಿಯಂ‌ಗು 2007ಡ್ ರಾಷ್ಟ್ರೀಯ ಸ್ಥಾನಮಾನ ಕೊರಿಯೆರ್.<ref name="KyivMuseum"/> == ಇತ್ತೆದ ಸ್ಥಿತಿ == ಇತ್ತೆ ಕೀವ್ ಕೋಟೆದ ಮಸ್ತ್ ಐತಿಹಾಸಿಕ ರಚನೆಲು ಸಂರಕ್ಷಿತ ಆದುಂಡು. ಕೆಲವು ರಚನೆಲು ವಸ್ತುಸಂಗ್ರಹಾಲಯೊದ ಭಾಗ ಆದುಂಡು, ಬಾಕಿ ಕೆಲವು ಕಟ್ಟಡೊಲು ಬೇತೆ ಬಳಕೆಲೆಗ್ ಒಳಪಟ್ಟುದುಂಡು. ಕೀವ್ ನಗರೊದ ಅಧಿಕೃತ ಪ್ರವಾಸಿ ಮಾರ್ಗದರ್ಶಿ ಕೀವ್ ಕೋಟೆನು ವಿಶ್ವೊಡು ಒರಿದಿನ ಮಲ್ಲ ಕೋಟೆ ಸಮುಚ್ಚಯೊಲೆಡ್ ಉಂದು ಒಂಜಿ ಪಂಡ್ದ್ ವಿವರಿಸಾವೆರ್. ಇಂಚ ಕೋಟೆದ ಗೋಡೆ, ರಕ್ಷಣಾ ದಂಡೆಲು, ಕ್ಯಾಪೊನಿಯರ್‌ಲು ಬೊಕ್ಕ ಲೈಸಾ ಹೊರಾ ಕೋಟೆದ ಬಯಲು ಪ್ರದರ್ಶನೊಲಾ ಉಂಡು.<ref name="KyivGuide"/> == ಕೋಟೆಡ್ ಸೇವೆ ಮಲ್ತಿನ ಗಮನಾರ್ಹ ವ್ಯಕ್ತಿಲು == * ಕಾನ್ಸ್ಟಾಂಟೈನ್ ಯಿಪ್ಸಿಲಾಂಟಿಸ್ – 1807 ಬೊಕ್ಕ 1816 ನಡುಟು ಪೆಚೆರ್ಸ್ಕ್ ಕೋಟೆದ ಕಮಾಂಡೆಂಟ್ ಆದ್ ಸೇವೆ ಮಲ್ತೆರ್. == ಚಿತ್ರೊಲೆನ್ ಗ್ಯಾಲರಿ == <gallery widths="180"> File:Kiev-Fortress22-03-2011.jpg|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಮುಖ್ಯ ಬಾಗಿಲು ಬೊಕ್ಕ ಗೋಡೆ — ಪೂರ್ವೊದ ಕಡೆಡ್ದ್ File:Hospital fort of the Kiev fortress.jpg|ಹಾಸ್ಪಿಟಲ್ ಕೋಟೆದ ಮುಖ್ಯ ಬಾಗಿಲು ಬೊಕ್ಕ ಗೋಡೆ — ಪಶ್ಚಿಮೊದ ಕಡೆಡ್ದ್ File:Kosoj Caponier of the Kiev fortress.jpg|ಕೊಸಿ ಕ್ಯಾಪೊನಿಯರ್ File:Башня №1 (редюит) Васильковского укрепления.JPG|ಗೋಪುರ ಸಂಖ್ಯೆ ೧ — ರೆಡ್ಯೂಯಿಟ್, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ File:Круглая башня (№ 2) Васильковского укрепления.JPG|ವೃತ್ತಾಕಾರ ಗೋಪುರ ಸಂಖ್ಯೆ ೨, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ File:Башня № 3 (Прозоровская).JPG|ಪ್ರೊಜೊರಿವ್ಸ್ಕಾ ಗೋಪುರ ಸಂಖ್ಯೆ ೩, ವಾಸಿಲ್ಕಿವ್ ಕೋಟೆ </gallery> == ಇಂಚಲಾ ತೂಲೆ == * ಕ್ಲೋವ್ ಅರಮನೆ * ಕೀವ್ ಪೆಚೆರ್ಸ್ಕ್ ಲಾವ್ರಾ * ಕೀವ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು == ಉಲ್ಲೇಕೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲು == * [https://www.kyiv-fortress.org.ua/ ರಾಷ್ಟ್ರೀಯ ಐತಿಹಾಸಿಕ-ವಾಸ್ತುಶಿಲ್ಪ ವಸ್ತುಸಂಗ್ರಹಾಲಯ "ಕೀವ್ ಕೋಟೆ" — ಅಧಿಕೃತ ಜಾಲತಾಣ] * {{Commonscat|Kyiv Fortress}} [[Category:ಕೀವ್‌ದ ಕಟ್ಟಡೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಕೋಟೆಲು]] [[Category:ಕೀವ್‌ದ ಐತಿಹಾಸಿಕ ತಾಣೊಲು]] [[Category:ಉಕ್ರೇನ್‌ದ ಸೈನಿಕ ವಾಸ್ತುಶಿಲ್ಪ]] [[Category:ಉಕ್ರೇನ್‌ದ ವಸ್ತುಸಂಗ್ರಹಾಲಯೊಲು]] [[Category:ಕೀವ್‌ದ ಪ್ರವಾಸಿ ಆಕರ್ಷಣೆಲು]] 9lv3d5v3zeuv36rs1ae1ivapgcot8zq ಬಳಕೆದಾರೆ ಪಾತೆರ:PrathikRamesh11 3 28043 366171 2026-08-21T09:40:05Z ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ 2534 ಪೊಸ ಸದಸ್ಯೆರೆ ಚರ್ಚಾಪುಟೊಡು [[Template:Welcome|ಸ್ವಾಗತ ಸಂದೇಸೊನು]]ಸೇರ್ಸಾಯರ ಆಪುಂಡು 366171 wikitext text/x-wiki {{Template:Welcome|realName=|name=PrathikRamesh11}} -- [[ಬಳಕೆದಾರೆ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ]] ([[ಬಳಕೆದಾರೆ ಪಾತೆರ:ತುಳು ವಿಕಿಪೀಡಿಯ ಸಮುದಾಯೊ|ಪಾತೆರ್ಲೆ]]) ೧೫:೧೦, ೨೧ ಆಗೋಸ್ಟು ೨೦೨೬ (IST) d4wa15apo9la6k3xzrvdxc33j4ha4a0 ಟೆಂಪ್ಲೇಟ್:In use 10 28044 366178 2025-11-07T01:30:01Z en>Andrybak 0 add parameter "nocat" for [[WP:NOCAT|category suppression]] 366178 wikitext text/x-wiki {{Mbox | name = In use | type = notice | class = ambox-In_use | image = [[File:Ambox clock.svg|48px|alt=|link=]] | css = margin: 1px | text = This {{#ifeq:{{{1|}}}|section|[[Help:Section|section]]|{{#switch:{{NAMESPACE}} | {{ns:0}} = article | Talk = [[Help:Talk page|talk page]] | Category = [[Wikipedia:Category|category page]] | Help = [[Help:Contents|help page]] | Portal = [[Wikipedia:Portal|portal page]] | Template = [[Wikipedia:Template messages|template page]] | User = [[Wikipedia:User page|user page]] | User talk = [[Wikipedia:User page|user talk page]] | Wikipedia = [[Wikipedia:Project namespace|project page]] | Wikipedia talk = [[Wikipedia:Project namespace|project talk page]] }}}} is '''actively undergoing a [[Wikipedia:Edit lock|major edit]]''' for {{#ifeq:{{{1|}}}|section|{{{2|a little while}}}|{{{1|a little while}}}}}. To help avoid [[Help:Edit conflict|edit conflicts]], do not edit it while this notice is displayed.{{pb}}<small>{{#if:{{{time|}}}|This notice was added at {{{time|}}}.|}} This page was last edited {{time ago|{{REVISIONTIMESTAMP}}}}, at {{#time:H:i, j F Y|{{REVISIONTIMESTAMP}}}} (UTC) ({{Purge|refresh}}). Please remove this template if [[Special:PageHistory/{{FULLPAGENAME}}|this page hasn't been edited]] for a significant time. If you added this notice, remember to remove it or replace it with {{tl|under construction}} between editing sessions.</small> <includeonly>{{#if:{{Yesno|{{{nocat|}}}}}||{{#ifeq:{{{category}}}|no||{{#switch:{{NAMESPACE}} | User | User talk = <!-- no category --> | [[Category:Pages actively undergoing a major edit]]}}}}}}</includeonly> }}<noinclude> {{Documentation}} </noinclude> pijjoxsuo84u0ch6yad8s6et9i1dqen 366179 366178 2026-08-21T10:06:44Z ChiK 1136 ೧ revisions imported from [[:en:Template:In_use]] 366178 wikitext text/x-wiki {{Mbox | name = In use | type = notice | class = ambox-In_use | image = [[File:Ambox clock.svg|48px|alt=|link=]] | css = margin: 1px | text = This {{#ifeq:{{{1|}}}|section|[[Help:Section|section]]|{{#switch:{{NAMESPACE}} | {{ns:0}} = article | Talk = [[Help:Talk page|talk page]] | Category = [[Wikipedia:Category|category page]] | Help = [[Help:Contents|help page]] | Portal = [[Wikipedia:Portal|portal page]] | Template = [[Wikipedia:Template messages|template page]] | User = [[Wikipedia:User page|user page]] | User talk = [[Wikipedia:User page|user talk page]] | Wikipedia = [[Wikipedia:Project namespace|project page]] | Wikipedia talk = [[Wikipedia:Project namespace|project talk page]] }}}} is '''actively undergoing a [[Wikipedia:Edit lock|major edit]]''' for {{#ifeq:{{{1|}}}|section|{{{2|a little while}}}|{{{1|a little while}}}}}. To help avoid [[Help:Edit conflict|edit conflicts]], do not edit it while this notice is displayed.{{pb}}<small>{{#if:{{{time|}}}|This notice was added at {{{time|}}}.|}} This page was last edited {{time ago|{{REVISIONTIMESTAMP}}}}, at {{#time:H:i, j F Y|{{REVISIONTIMESTAMP}}}} (UTC) ({{Purge|refresh}}). Please remove this template if [[Special:PageHistory/{{FULLPAGENAME}}|this page hasn't been edited]] for a significant time. If you added this notice, remember to remove it or replace it with {{tl|under construction}} between editing sessions.</small> <includeonly>{{#if:{{Yesno|{{{nocat|}}}}}||{{#ifeq:{{{category}}}|no||{{#switch:{{NAMESPACE}} | User | User talk = <!-- no category --> | [[Category:Pages actively undergoing a major edit]]}}}}}}</includeonly> }}<noinclude> {{Documentation}} </noinclude> pijjoxsuo84u0ch6yad8s6et9i1dqen ಟೆಂಪ್ಲೇಟ್:In use/doc 10 28045 366182 2026-08-19T13:43:42Z en>CapnZapp 0 /* Removal */ per the bot's documentation 366182 wikitext text/x-wiki {{Documentation subpage}} {{tsh|Iu|WIP|Editing}} {{notice|Remember to remove this template or replace it with {{Template link|Under construction}} as soon as you're finished editing. Anyone is allowed to remove the template two hours after the time of last edit.}} This [[Wikipedia:Template messages|template notice]] is intended for pages '''actively''' undergoing a major edit process. When this message is placed, the article remains open to editing, but courteous users should leave it alone until you're done. Bots such as [[User:AnomieBOT]] usually honor this template and don't make changes until it's removed. Wikipedia does not use pre-emptive software [[Wikipedia:Edit lock|article locks]] on pages, because this would be contrary to the policies of Wikipedia and spirit of openness. You are expected to remove this message in a relatively short while. If you plan to take a break from your major edit activity longer than two hours, please replace it with {{Template link|Under construction}} or remove it during the break. == Usage == {{see also|Help:Editing|Wikipedia:Template messages|Wikipedia:Template messages/Maintenance}} The template is placed at the top of a page you are actively editing for a short period of time, no greater than a few hours at a time. The tag is intended to inform people and bots that someone is currently working on the article, thereby reducing edit conflicts. Please do not leave it in place for longer than necessary, as doing so may unnecessarily discourage others from contributing to the article. Specifying periods of around a day or longer for this template goes against the spirit of simply avoiding edit conflicts; please only use it for sessions where you are actively editing the article. Using this template will place the including article in [[:Category:Pages actively undergoing a major edit]]. ;Basic usage <code><nowiki>{{In use}}</nowiki></code> This puts a notice saying the major edit is undergoing "for a little while", adding the time of last edit. ;Estimated time needed <code><nowiki>{{In use|40 minutes}}</nowiki></code> This replaces "a little while" with the supplied unnamed parameter. In this case you believe you need about 40 minutes (from the time of last edit), signaling that any user may remove this notice after such time. ;This notice was added <code><nowiki>{{In use|time=~~~~~}}</nowiki></code> The {{para|time}} parameter adds the text "This notice was added" followed by the value of the parameter at the start of the notice. While you could supply any value, you are recommended to supply the current time stamp: <code><nowiki>{{In use|time=~~~~~}}</nowiki></code> at the top of any page you want to work on. The five tildes converts to a datestamp of the current time (see [[WP:5TILDES]]). ; section notice <code><nowiki>{{In use|section}}</nowiki></code> If a specific section of the page is being edited, adding "section" as the first unnamed parameter changes the notice from "This article" (or "this template" etc) to "This section". You can alternatively use {{tlx|In use section}}. It used to be a stand-alone template but when this template gained the section notice ability in 2009 it was converted to a call to {{tlx|In use|section}} with "a little while" supplied as the default parameter for the estimated time needed unnamed parameter. While currently superfluous since this template's default message is the same, the defaults ''are'' independent of each other. ; section notice and estimated time needed <code><nowiki>{{In use|section|40 minutes}}</nowiki></code> If you want to estimate a time needed for a section notice, you must supply "section" as the '''first''' unnamed parameter. ; all three parameters <code><nowiki>{{In use|section|40 minutes|time=~~~~~}}</nowiki></code> == Alternatives == If you wish to indicate that an article is being rebuilt over a longer period of time consider the {{Tlx|Under construction}} template. That template encourages others to edit the article while indicating that it is a [[Wikipedia:Editing policy|work in progress]]. === "Freezing" a page from further edits === {{main|Wikipedia:Edit lock}} Please be aware that this template does not actually prevent further edits or lock out other editors. It ''asks'' other editors to hold off editing as a ''courtesy''. Wikipedia does not use pre-emptive software article locks on pages, because this would be contrary to the policies of Wikipedia and spirit of openness. Page protection is never to be used for closing down an article's normal development and community editing, and should be used only to prevent actual disruption. === Accessing a "frozen" or stable page version === So you cannot or should not freeze pages. But what about accessing a stable page version? For instance, if you are creating a spoken Wikipedia reading of an article, you definitely want to access the one and same version of that page between recording sessions; you don't want the text you are recording to change half-way through. Wikipedia offers the [[Help:Permanent link]] functionality, where you are assured page stability. Instead of working on the regular version of any article, first click "Permanent link" which you will find in the Tools section of the left sidebar of each Wikipedia article. You can access this page, share it with others, bookmark it etc and you will get the exact same version of the page each time, no matter how much other editors change the actual live page. == Removal == If this template has been left in place for more than two hours <strong>since the last edit</strong> as reported by this template. This template uses {{tl|time ago}} to round and display time passed, so once the time since last edit exceeds 2 1/2 hours, the message changes from "2 hours ago" to "3 hours ago", and you can safely assume the placing editor has forgotten to remove it, and you may remove it yourself. [[User:JL-Bot|JL-Bot]] will remove this template if the last edit (not the timestamp when the template was added, the timestamp of any edit) was more than 1 days ago. == TemplateData == <templatedata> { "params": { "1": {}, "2": {}, "time": { "required": true, "autovalue": "~~~~~" }, "category": {} }, "format": "inline" } </templatedata> == See also == * {{Tl|In use section}}, section-specific version of this template * {{Tl|Closing}}, similar template used to notify readers that a discussion closure is taking place * {{Tl|In creation}} to use this template when multiple edits are being made to add content immediately after creating the article * {{T1|recently revised}}, if you are unsure about your revision/major changes and want a second opinion * {{Tl|Under construction}} for articles actively undergoing construction to list them in [[:Category:Pages actively undergoing construction]] * [[Wikipedia:Deletion of newly created pages]], an essay urging not to list pages having {{tl|New page}} for deletion, unless they have been abandoned or contain serious infractions * {{t|Being translated}} * [[Wikipedia:Template index/Work in progress]] – a page listing similar templates <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Under-construction templates|{{PAGENAME}}]] }}</includeonly> h3x91zu4vnx0t9sh2k622nanc4f9oc4 366183 366182 2026-08-21T10:06:46Z ChiK 1136 ೧ revisions imported from [[:en:Template:In_use/doc]] 366182 wikitext text/x-wiki {{Documentation subpage}} {{tsh|Iu|WIP|Editing}} {{notice|Remember to remove this template or replace it with {{Template link|Under construction}} as soon as you're finished editing. Anyone is allowed to remove the template two hours after the time of last edit.}} This [[Wikipedia:Template messages|template notice]] is intended for pages '''actively''' undergoing a major edit process. When this message is placed, the article remains open to editing, but courteous users should leave it alone until you're done. Bots such as [[User:AnomieBOT]] usually honor this template and don't make changes until it's removed. Wikipedia does not use pre-emptive software [[Wikipedia:Edit lock|article locks]] on pages, because this would be contrary to the policies of Wikipedia and spirit of openness. You are expected to remove this message in a relatively short while. If you plan to take a break from your major edit activity longer than two hours, please replace it with {{Template link|Under construction}} or remove it during the break. == Usage == {{see also|Help:Editing|Wikipedia:Template messages|Wikipedia:Template messages/Maintenance}} The template is placed at the top of a page you are actively editing for a short period of time, no greater than a few hours at a time. The tag is intended to inform people and bots that someone is currently working on the article, thereby reducing edit conflicts. Please do not leave it in place for longer than necessary, as doing so may unnecessarily discourage others from contributing to the article. Specifying periods of around a day or longer for this template goes against the spirit of simply avoiding edit conflicts; please only use it for sessions where you are actively editing the article. Using this template will place the including article in [[:Category:Pages actively undergoing a major edit]]. ;Basic usage <code><nowiki>{{In use}}</nowiki></code> This puts a notice saying the major edit is undergoing "for a little while", adding the time of last edit. ;Estimated time needed <code><nowiki>{{In use|40 minutes}}</nowiki></code> This replaces "a little while" with the supplied unnamed parameter. In this case you believe you need about 40 minutes (from the time of last edit), signaling that any user may remove this notice after such time. ;This notice was added <code><nowiki>{{In use|time=~~~~~}}</nowiki></code> The {{para|time}} parameter adds the text "This notice was added" followed by the value of the parameter at the start of the notice. While you could supply any value, you are recommended to supply the current time stamp: <code><nowiki>{{In use|time=~~~~~}}</nowiki></code> at the top of any page you want to work on. The five tildes converts to a datestamp of the current time (see [[WP:5TILDES]]). ; section notice <code><nowiki>{{In use|section}}</nowiki></code> If a specific section of the page is being edited, adding "section" as the first unnamed parameter changes the notice from "This article" (or "this template" etc) to "This section". You can alternatively use {{tlx|In use section}}. It used to be a stand-alone template but when this template gained the section notice ability in 2009 it was converted to a call to {{tlx|In use|section}} with "a little while" supplied as the default parameter for the estimated time needed unnamed parameter. While currently superfluous since this template's default message is the same, the defaults ''are'' independent of each other. ; section notice and estimated time needed <code><nowiki>{{In use|section|40 minutes}}</nowiki></code> If you want to estimate a time needed for a section notice, you must supply "section" as the '''first''' unnamed parameter. ; all three parameters <code><nowiki>{{In use|section|40 minutes|time=~~~~~}}</nowiki></code> == Alternatives == If you wish to indicate that an article is being rebuilt over a longer period of time consider the {{Tlx|Under construction}} template. That template encourages others to edit the article while indicating that it is a [[Wikipedia:Editing policy|work in progress]]. === "Freezing" a page from further edits === {{main|Wikipedia:Edit lock}} Please be aware that this template does not actually prevent further edits or lock out other editors. It ''asks'' other editors to hold off editing as a ''courtesy''. Wikipedia does not use pre-emptive software article locks on pages, because this would be contrary to the policies of Wikipedia and spirit of openness. Page protection is never to be used for closing down an article's normal development and community editing, and should be used only to prevent actual disruption. === Accessing a "frozen" or stable page version === So you cannot or should not freeze pages. But what about accessing a stable page version? For instance, if you are creating a spoken Wikipedia reading of an article, you definitely want to access the one and same version of that page between recording sessions; you don't want the text you are recording to change half-way through. Wikipedia offers the [[Help:Permanent link]] functionality, where you are assured page stability. Instead of working on the regular version of any article, first click "Permanent link" which you will find in the Tools section of the left sidebar of each Wikipedia article. You can access this page, share it with others, bookmark it etc and you will get the exact same version of the page each time, no matter how much other editors change the actual live page. == Removal == If this template has been left in place for more than two hours <strong>since the last edit</strong> as reported by this template. This template uses {{tl|time ago}} to round and display time passed, so once the time since last edit exceeds 2 1/2 hours, the message changes from "2 hours ago" to "3 hours ago", and you can safely assume the placing editor has forgotten to remove it, and you may remove it yourself. [[User:JL-Bot|JL-Bot]] will remove this template if the last edit (not the timestamp when the template was added, the timestamp of any edit) was more than 1 days ago. == TemplateData == <templatedata> { "params": { "1": {}, "2": {}, "time": { "required": true, "autovalue": "~~~~~" }, "category": {} }, "format": "inline" } </templatedata> == See also == * {{Tl|In use section}}, section-specific version of this template * {{Tl|Closing}}, similar template used to notify readers that a discussion closure is taking place * {{Tl|In creation}} to use this template when multiple edits are being made to add content immediately after creating the article * {{T1|recently revised}}, if you are unsure about your revision/major changes and want a second opinion * {{Tl|Under construction}} for articles actively undergoing construction to list them in [[:Category:Pages actively undergoing construction]] * [[Wikipedia:Deletion of newly created pages]], an essay urging not to list pages having {{tl|New page}} for deletion, unless they have been abandoned or contain serious infractions * {{t|Being translated}} * [[Wikipedia:Template index/Work in progress]] – a page listing similar templates <includeonly>{{Sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Under-construction templates|{{PAGENAME}}]] }}</includeonly> h3x91zu4vnx0t9sh2k622nanc4f9oc4 ಟೆಂಪ್ಲೇಟ್:Being created 10 28046 366185 2026-08-07T12:12:26Z en>FaviFake 0 fix 366185 wikitext text/x-wiki <onlyinclude>{{Mbox | type = notice | image = [[File:Ambox clock.svg|48px|alt=|link=]] | css = margin: 1px | text = '''This new {{{page|Wikipedia article}}} is [[Help:Wikipedia: The Missing Manual/Editing, creating, and maintaining articles/Creating a new article|currently being created]] through collaboration and successive edits.''' {{#ifeq:{{NAMESPACE}}|0|If you have concerns about [[WP:GNG|notability]], [[WP:MOS|style]], or other issues, please comment on the [[WP:TPG|{{{tp|talk page}}}]] of the {{{article|article}}} or {{{creator|the creator}}}.|}}To help avoid [[Help:Edit conflict|edit conflicts]] and [[WP:{{{agf|Assume good faith}}}|other confusion]], {{{creator|the creator}}} [[Wikipedia:Please do not bite the newcomers|asks]] that for {{{eta|a short time}}} this {{{page|page}}} not be edited unnecessarily, or [[WP:{{{xfd|Articles for deletion}}}|nominated for deletion]] during this early stage of development.{{Pb}}{{smalldiv|{{#if:{{{time|}}}|This message was added at {{{time|}}}.|}} {{last edited by}} Please remove this notice if the {{{page|page}}} [{{fullurl:{{FULLPAGENAME}}|action=history}} has not been edited] for {{{duration|several hours}}}.}}}} {{#ifeq:{{{category}}}|no||{{#switch:{{NAMESPACE}} | User | User talk = <!-- no category --> | [[Category:Pages actively undergoing a major edit|{{PAGENAME}}]]}}}}</onlyinclude><noinclude> {{Documentation}} </noinclude> 9qt7t5gfn7ti284k9whhz97qycosoor 366186 366185 2026-08-21T10:14:29Z ChiK 1136 ೧ revisions imported from [[:en:Template:Being_created]] 366185 wikitext text/x-wiki <onlyinclude>{{Mbox | type = notice | image = [[File:Ambox clock.svg|48px|alt=|link=]] | css = margin: 1px | text = '''This new {{{page|Wikipedia article}}} is [[Help:Wikipedia: The Missing Manual/Editing, creating, and maintaining articles/Creating a new article|currently being created]] through collaboration and successive edits.''' {{#ifeq:{{NAMESPACE}}|0|If you have concerns about [[WP:GNG|notability]], [[WP:MOS|style]], or other issues, please comment on the [[WP:TPG|{{{tp|talk page}}}]] of the {{{article|article}}} or {{{creator|the creator}}}.|}}To help avoid [[Help:Edit conflict|edit conflicts]] and [[WP:{{{agf|Assume good faith}}}|other confusion]], {{{creator|the creator}}} [[Wikipedia:Please do not bite the newcomers|asks]] that for {{{eta|a short time}}} this {{{page|page}}} not be edited unnecessarily, or [[WP:{{{xfd|Articles for deletion}}}|nominated for deletion]] during this early stage of development.{{Pb}}{{smalldiv|{{#if:{{{time|}}}|This message was added at {{{time|}}}.|}} {{last edited by}} Please remove this notice if the {{{page|page}}} [{{fullurl:{{FULLPAGENAME}}|action=history}} has not been edited] for {{{duration|several hours}}}.}}}} {{#ifeq:{{{category}}}|no||{{#switch:{{NAMESPACE}} | User | User talk = <!-- no category --> | [[Category:Pages actively undergoing a major edit|{{PAGENAME}}]]}}}}</onlyinclude><noinclude> {{Documentation}} </noinclude> 9qt7t5gfn7ti284k9whhz97qycosoor ಟೆಂಪ್ಲೇಟ್:Being created/doc 10 28047 366189 2026-08-19T15:02:26Z en>FaviFake 0 /* */ main??? 366189 wikitext text/x-wiki {{Documentation subpage}} <p>{{Tl|Being created}} is used on pages that have been created and are being added to by a series of incremental improvements. Its intended use is for editors who have made an article and will be adding content within a matter of minutes or hours. If readers see this on an article it shows that the creation was premature. Editors should consider using a sandbox instead.</p><p>Articles which have already been created by other editors, already existing in [[WP:NS0|article space]], can use the {{tl|Being created}} template unless they determine the {{txl|under construction}} template to be more appropriate.</p> [[User:JL-Bot|JL-Bot]] will remove this template if the last edit (not the timestamp when the template was added, the timestamp of any edit) was more than 7 days ago. == Usage == * The basic syntax for using this template is<nowiki>:</nowiki> *: {{tlx|Being created|<nowiki>time=~~~~~</nowiki>}} * This renders a notice similar to the following example<nowiki>:</nowiki> {{Being created|time=07:52, 24 January 2013 (UTC)|category=no}} * The following optional parameters may be use, as shown<nowiki>:</nowiki> *: <syntaxhighlight lang="wikitext"> {{Being created|time=~~~~~ | page = [page] | new = [Wikipedia article] | tp = [talk page] | article = [article] | creator = [the creator] | eta = [a short time] | duration = [several hours] | agf = ABF | xfd = MFD }}</syntaxhighlight> *:*{{small|Note {{para|agf|ABF}} ''and'' {{para|xfd|MFD}} ''are internal parameters that can change the '''[[WP:SC|shortcut]]''', to modify the associated link''. {{para|agf|}} ''defaults to '''[[WP:AGF]]''' linking '''[[WP:AGF|other confusion]]'''. The second example changes the link to '''[[WP:ABF]]''' rendering the link as '''[[WP:ABF|other confusion]]'''''. {{para|xfd|}} ''defaults to '''[[WP:AFD]]''' linking '''[[WP:AFD|nominated for deletion]]'''. The second example makes the link '''[[WP:MFD]]'''''.}} * Compare the following example<nowiki>:</nowiki> {{Being created|time=07:52, 24 January 2013 (UTC) | page = [page] | new = [Wikipedia article] | tp = [talk page] | article = [article] | creator = [the creator] | eta = [a short time] | duration = [several hours] | agf = ABF | xfd = MFD | category = no }} {{center|'''<em>Please remember to remove the template-code as soon as you're finished editing</em> !!!'''}} == Results summary == <p>A [[Wikipedia:Template messages|template message]] will be placed at the top of the article. This template is a '''courtesy template''', intended to inform people that another editor is actively creating the article. It is intended to note to other editors that reviewing or deleting the article would be premature.</p><p>Do not leave this template in an article unless you are actively editing it. Half an hour to an hour between edits is a suggested limit. Other editors, especially those on [[WP:NPP|new page patrol]], may be inconvenienced if it is left on, and it may inadvertently discourage others from contributing to the article. Specifying periods of hours or longer for this template [[WP:OWN|goes against the spirit]] of simply avoiding edit conflicts and procedural problems, and the template may be removed if this is the case, or if the article is otherwise of concern.</p><p>This template is not an excuse for constructing articles about non-notable subjects. If an article appears to be [[WP:SPAM|spam]], [[WP:PROMO|blatant advertising]], a [[WP:COPYVIO|copyright violation]], or a [[WP:BLP|biography of a living person]], or any other fundamental policy, it may be blanked or deleted despite the presence of the tag. ''The tag is a courtesy to other editors, not an excuse for bad editing.''</p><p>If you wish to create a new article over a longer period of time consider using the {{tl|Under construction}} template. That template encourages others to edit the article while still indicating that it is a [[Wikipedia:Editing policy|work in progress]].</p> == See also == * {{tl|In use}}, for articles actively undergoing a major edit; listing them in [[:Category:Pages actively undergoing a major edit]] * {{T1|recently revised}}, if you are unsure about your revision/major changes and want a second opinion * {{tl|Under construction}}, for articles actively undergoing construction; listing them in [[:Category:Pages actively undergoing construction]] * [[Wikipedia:Deletion of pages under construction|Deletion of pages under construction]], an essay/failed proposal urging not to list pages tagged with {{tl|Under construction}} for deletion unless they have not been edited for a week, or they contain serious infractions * {{t|Being translated}} <includeonly>{{Sandbox other|| [[Category:Under-construction templates|{{PAGENAME}}]] }}</includeonly> 3mdx987wqz9laskkenx494z7uzd472j 366190 366189 2026-08-21T10:14:30Z ChiK 1136 ೧ revisions imported from [[:en:Template:Being_created/doc]] 366189 wikitext text/x-wiki {{Documentation subpage}} <p>{{Tl|Being created}} is used on pages that have been created and are being added to by a series of incremental improvements. Its intended use is for editors who have made an article and will be adding content within a matter of minutes or hours. If readers see this on an article it shows that the creation was premature. Editors should consider using a sandbox instead.</p><p>Articles which have already been created by other editors, already existing in [[WP:NS0|article space]], can use the {{tl|Being created}} template unless they determine the {{txl|under construction}} template to be more appropriate.</p> [[User:JL-Bot|JL-Bot]] will remove this template if the last edit (not the timestamp when the template was added, the timestamp of any edit) was more than 7 days ago. == Usage == * The basic syntax for using this template is<nowiki>:</nowiki> *: {{tlx|Being created|<nowiki>time=~~~~~</nowiki>}} * This renders a notice similar to the following example<nowiki>:</nowiki> {{Being created|time=07:52, 24 January 2013 (UTC)|category=no}} * The following optional parameters may be use, as shown<nowiki>:</nowiki> *: <syntaxhighlight lang="wikitext"> {{Being created|time=~~~~~ | page = [page] | new = [Wikipedia article] | tp = [talk page] | article = [article] | creator = [the creator] | eta = [a short time] | duration = [several hours] | agf = ABF | xfd = MFD }}</syntaxhighlight> *:*{{small|Note {{para|agf|ABF}} ''and'' {{para|xfd|MFD}} ''are internal parameters that can change the '''[[WP:SC|shortcut]]''', to modify the associated link''. {{para|agf|}} ''defaults to '''[[WP:AGF]]''' linking '''[[WP:AGF|other confusion]]'''. The second example changes the link to '''[[WP:ABF]]''' rendering the link as '''[[WP:ABF|other confusion]]'''''. {{para|xfd|}} ''defaults to '''[[WP:AFD]]''' linking '''[[WP:AFD|nominated for deletion]]'''. The second example makes the link '''[[WP:MFD]]'''''.}} * Compare the following example<nowiki>:</nowiki> {{Being created|time=07:52, 24 January 2013 (UTC) | page = [page] | new = [Wikipedia article] | tp = [talk page] | article = [article] | creator = [the creator] | eta = [a short time] | duration = [several hours] | agf = ABF | xfd = MFD | category = no }} {{center|'''<em>Please remember to remove the template-code as soon as you're finished editing</em> !!!'''}} == Results summary == <p>A [[Wikipedia:Template messages|template message]] will be placed at the top of the article. This template is a '''courtesy template''', intended to inform people that another editor is actively creating the article. It is intended to note to other editors that reviewing or deleting the article would be premature.</p><p>Do not leave this template in an article unless you are actively editing it. Half an hour to an hour between edits is a suggested limit. Other editors, especially those on [[WP:NPP|new page patrol]], may be inconvenienced if it is left on, and it may inadvertently discourage others from contributing to the article. Specifying periods of hours or longer for this template [[WP:OWN|goes against the spirit]] of simply avoiding edit conflicts and procedural problems, and the template may be removed if this is the case, or if the article is otherwise of concern.</p><p>This template is not an excuse for constructing articles about non-notable subjects. If an article appears to be [[WP:SPAM|spam]], [[WP:PROMO|blatant advertising]], a [[WP:COPYVIO|copyright violation]], or a [[WP:BLP|biography of a living person]], or any other fundamental policy, it may be blanked or deleted despite the presence of the tag. ''The tag is a courtesy to other editors, not an excuse for bad editing.''</p><p>If you wish to create a new article over a longer period of time consider using the {{tl|Under construction}} template. That template encourages others to edit the article while still indicating that it is a [[Wikipedia:Editing policy|work in progress]].</p> == See also == * {{tl|In use}}, for articles actively undergoing a major edit; listing them in [[:Category:Pages actively undergoing a major edit]] * {{T1|recently revised}}, if you are unsure about your revision/major changes and want a second opinion * {{tl|Under construction}}, for articles actively undergoing construction; listing them in [[:Category:Pages actively undergoing construction]] * [[Wikipedia:Deletion of pages under construction|Deletion of pages under construction]], an essay/failed proposal urging not to list pages tagged with {{tl|Under construction}} for deletion unless they have not been edited for a week, or they contain serious infractions * {{t|Being translated}} <includeonly>{{Sandbox other|| [[Category:Under-construction templates|{{PAGENAME}}]] }}</includeonly> 3mdx987wqz9laskkenx494z7uzd472j ಟೆಂಪ್ಲೇಟ್:In creation 10 28048 366191 2026-08-07T12:10:51Z en>FaviFake 0 FaviFake moved page [[Template:In creation]] to [[Template:Being created]] over redirect: more similar to [[template:being translated]], [[template:being merged]], [[template:being split]], [[template:being archived]], etc and clearer (closing is more vague 366191 wikitext text/x-wiki #REDIRECT [[Template:Being created]] {{Redirect category shell| {{R from move}} }} 85qw4gdfa3o4gouxf55h56l2ruc6z6y 366192 366191 2026-08-21T10:15:05Z ChiK 1136 ೧ revisions imported from [[:en:Template:In_creation]] 366191 wikitext text/x-wiki #REDIRECT [[Template:Being created]] {{Redirect category shell| {{R from move}} }} 85qw4gdfa3o4gouxf55h56l2ruc6z6y ಪಾತೆರ:ಜಲೇಹ್ ಎಸ್ಫಹಾನಿ 1 28049 366197 2026-08-21T10:22:13Z ChiK 1136 ಹೊಸ ಪುಟ: {{ವಿಕಿಪೀಡಿಯ ಏಷ್ಯನ್ ತಿಂಗೊಲು 2024}} 366197 wikitext text/x-wiki {{ವಿಕಿಪೀಡಿಯ ಏಷ್ಯನ್ ತಿಂಗೊಲು 2024}} 3utjtlwxrbfox5govjpeg03ey77ejcd ಟೆಂಪ್ಲೇಟ್:Draft categories 10 28050 366206 2024-09-26T21:37:37Z en>Ymblanter 0 Changed protection settings for "[[Template:Draft categories]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)) 366206 wikitext text/x-wiki {{main other |1={{#invoke:String|replace|source={{{1|}}}|pattern=:[Cc]ategory|replace=Category|plain=false}}[[Category:Articles using draft categories]] |2=<div class="catlinks"><div style="font-size: small">This page will be placed in the following categories if it is moved to the [[Wikipedia:What is an article?#Namespace|article namespace]].</div>[[Help:Category|Categories]]: <ul style="margin: 0">{{#invoke:String|replace|source={{#invoke:String|replace|source={{{1|}}}|pattern=%{{!}}(.-)%]|replace=]|plain=false}}|pattern=%[%[:?[Cc]ategory:(.-)%]%]|replace=<li>[[:Category:%1|%1]]</li>|plain=false}}</ul></div> }}<noinclude> {{documentation}} </noinclude> nfsbngor70hqvvqb6q0bw49qdftrb48 366207 366206 2026-08-21T10:44:24Z ChiK 1136 ೧ revisions imported from [[:en:Template:Draft_categories]] 366206 wikitext text/x-wiki {{main other |1={{#invoke:String|replace|source={{{1|}}}|pattern=:[Cc]ategory|replace=Category|plain=false}}[[Category:Articles using draft categories]] |2=<div class="catlinks"><div style="font-size: small">This page will be placed in the following categories if it is moved to the [[Wikipedia:What is an article?#Namespace|article namespace]].</div>[[Help:Category|Categories]]: <ul style="margin: 0">{{#invoke:String|replace|source={{#invoke:String|replace|source={{{1|}}}|pattern=%{{!}}(.-)%]|replace=]|plain=false}}|pattern=%[%[:?[Cc]ategory:(.-)%]%]|replace=<li>[[:Category:%1|%1]]</li>|plain=false}}</ul></div> }}<noinclude> {{documentation}} </noinclude> nfsbngor70hqvvqb6q0bw49qdftrb48 ಬಳಕೆದಾರೆ:Tol/Template/Used by TolBot 2 28051 366208 2021-12-15T00:42:31Z en>Tol 0 link to Meta 366208 wikitext text/x-wiki {{Mbox | type = notice | image = | text = This page is used by [[User:TolBot|TolBot]]{{#if:{{{task|}}}|'s [[:m:User:TolBot/Task {{{task}}}|Task {{{task}}}]]}}. Please notify [[User:Tol]], its operator, before making major or breaking changes. Thank you. {{{text|}}} }}{{#ifeq:{{SUBPAGENAME}}|doc||<includeonly>[[Category:Pages used by TolBot {{#if:{{{task|}}}|Task {{{task}}}}}]]</includeonly>}} sbeksvmf0m8jzvsem662e2gv0l6c1xy 366209 366208 2026-08-21T10:44:29Z ChiK 1136 ೧ revisions imported from [[:en:User:Tol/Template/Used_by_TolBot]] 366208 wikitext text/x-wiki {{Mbox | type = notice | image = | text = This page is used by [[User:TolBot|TolBot]]{{#if:{{{task|}}}|'s [[:m:User:TolBot/Task {{{task}}}|Task {{{task}}}]]}}. Please notify [[User:Tol]], its operator, before making major or breaking changes. Thank you. {{{text|}}} }}{{#ifeq:{{SUBPAGENAME}}|doc||<includeonly>[[Category:Pages used by TolBot {{#if:{{{task|}}}|Task {{{task}}}}}]]</includeonly>}} sbeksvmf0m8jzvsem662e2gv0l6c1xy ಟೆಂಪ್ಲೇಟ್:Draft categories/doc 10 28052 366210 2025-06-26T19:09:49Z en>Annh07 0 Reverted 1 edit by [[Special:Contributions/Meowzza|Meowzza]] ([[User talk:Meowzza|talk]]) to last revision by DB1729 366210 wikitext text/x-wiki {{Documentation subpage}} {{High-use}} {{tsh|draftcat}} {{lua|Module:String}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> This template can be used when non-article pages like drafts and [[WP:USERNOCAT|user pages]] have categories meant for articles. The categories will be displayed but the page will not be added to the categories. If the page is moved to mainspace then the categories will automatically work as normal but the template should be removed. == Usage == Wrap categories in this code: <syntaxhighlight lang="wikitext">{{Draft categories| ... }}</syntaxhighlight> Sort keys and leading colons are allowed in the categories. Only use this template around a list of categories and not around a whole page or transclusion. {{tl|Suppress categories}} can be used for that. == Tracking category == The category {{Cl|Articles using draft categories|count=yes|count_type=all}} tracks pages that are using the template in the [[WP:NS0|main namespace]]. This template should be removed from those articles. == Example == <syntaxhighlight lang="wikitext">{{Draft categories| [[Category:Star Wars characters| ]] [[Category:Star Wars]] [[Category:Star Wars lists|Characters]] [[:Category:Lists of Disney characters]] [[:Category:Lists of fictional military personnel|Star Wars]] }}</syntaxhighlight> The output looks similar to the real display of categories on normal pages. The above produces: {{Draft categories| [[Category:Star Wars characters| ]] [[Category:Star Wars]] [[Category:Star Wars lists|Characters]] [[:Category:Lists of Disney characters]] [[:Category:Lists of fictional military personnel|Star Wars]] }} <includeonly>{{sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Draft namespace templates]] [[Category:Wikipedia categorization|τDraft categories]] }}</includeonly> == TemplateData == {{TemplateData header}} <templatedata> { "params": { "1": { "label": "Categories", "required": true, "suggested": true, "example": "[[Category:Example 1]]", "type": "content" } }, "description": "Stores and displays article categories in drafts without adding them to the category", "format": "block" } </templatedata> == Notes == {{User:Tol/Template/Used by TolBot|task=4}} loa35zbrar7wl3rmww8hyqohs089o9q 366211 366210 2026-08-21T10:44:29Z ChiK 1136 ೧ revisions imported from [[:en:Template:Draft_categories/doc]] 366210 wikitext text/x-wiki {{Documentation subpage}} {{High-use}} {{tsh|draftcat}} {{lua|Module:String}} <!-- Please place categories where indicated at the bottom of this page and interwikis at Wikidata (see [[Wikipedia:Wikidata]]) --> This template can be used when non-article pages like drafts and [[WP:USERNOCAT|user pages]] have categories meant for articles. The categories will be displayed but the page will not be added to the categories. If the page is moved to mainspace then the categories will automatically work as normal but the template should be removed. == Usage == Wrap categories in this code: <syntaxhighlight lang="wikitext">{{Draft categories| ... }}</syntaxhighlight> Sort keys and leading colons are allowed in the categories. Only use this template around a list of categories and not around a whole page or transclusion. {{tl|Suppress categories}} can be used for that. == Tracking category == The category {{Cl|Articles using draft categories|count=yes|count_type=all}} tracks pages that are using the template in the [[WP:NS0|main namespace]]. This template should be removed from those articles. == Example == <syntaxhighlight lang="wikitext">{{Draft categories| [[Category:Star Wars characters| ]] [[Category:Star Wars]] [[Category:Star Wars lists|Characters]] [[:Category:Lists of Disney characters]] [[:Category:Lists of fictional military personnel|Star Wars]] }}</syntaxhighlight> The output looks similar to the real display of categories on normal pages. The above produces: {{Draft categories| [[Category:Star Wars characters| ]] [[Category:Star Wars]] [[Category:Star Wars lists|Characters]] [[:Category:Lists of Disney characters]] [[:Category:Lists of fictional military personnel|Star Wars]] }} <includeonly>{{sandbox other|| <!-- Categories below this line, please; interwikis at Wikidata --> [[Category:Draft namespace templates]] [[Category:Wikipedia categorization|τDraft categories]] }}</includeonly> == TemplateData == {{TemplateData header}} <templatedata> { "params": { "1": { "label": "Categories", "required": true, "suggested": true, "example": "[[Category:Example 1]]", "type": "content" } }, "description": "Stores and displays article categories in drafts without adding them to the category", "format": "block" } </templatedata> == Notes == {{User:Tol/Template/Used by TolBot|task=4}} loa35zbrar7wl3rmww8hyqohs089o9q ಒಲೆಸ್ಕೊ ಕೋಟೆ 0 28053 366213 2026-08-21T10:50:22Z PrathikRamesh11 10089 "[[:en:Special:Redirect/revision/1365001004|Olesko Castle]]" ಪುಟವನ್ನು ಅನುವಾದಿಸುವುದರಿಂದ ಸೃಷ್ಟಿಸಲಾಯಿತು 366213 wikitext text/x-wiki <templatestyles src="Module:Message box/ambox.css"></templatestyles> <templatestyles src="Module:Infobox/styles.css"></templatestyles>  '''ಒಲೆಸ್ಕೊ ಕೋಟೆ''' ( Ukrainian ; Polish ) ಉಕ್ರೇನ್ ಡ್ ಇತ್ತೆದ ಜೊಲೊಚಿವ್ ರಾಯನ್ ದ ಗಡಿಟ್ ಉಂಡು. ಕೋಟೆದ ಸುರುತ ಐತಿಹಾಸಿಕ ದಾಖಲೆ 1390 ಡ್ ಉಂಡು, ಅಪಗ ಪೋಪ್ ಬೊನಿಫೇಸ್ IX ಒಲೆಸ್ಕೊ ಬೊಕ್ಕ ತುಸ್ತಾನ್ ನ್ ಹಾಲಿಚ್ ದ ಒರಿ ಕ್ಯಾಥೊಲಿಕ್ ಬಿಷಪ್ ಗ್ ಕೊರ್ತೆರ್. ಉಂದು ಪಶ್ಚಿಮ ಉಕ್ರೇನ್ ದ ಮಲ್ಲ ನಗರ ಆಯಿನ ಲ್ವಿವ್ ಡ್ದ್ ಸುಮಾರ್ ಎಪ್ಪತ್ತೈನ್ ಕಿಲೋಮೀಟರ್ ದೂರೊಡು ಉಂಡು. <ref>{{Cite web|title=Музей-заповідник «Олеський замок» {{!}} Львівська національна галерея мистецтв імені Бориса Возницького|url=https://lvivgallery.org.ua/museums/muzey-zapovidnyk-oleskyy-zamok|accessdate=2023-08-03|website=lvivgallery.org.ua}}</ref> == ಸ್ಥಳ == [[ಫೈಲ್:OleskoZamokentrance.jpg|left|thumb|305x305px| ಒಲೆಸ್ಕೊ ಕೋಟೆದ ಪ್ರವೇಶ ದ್ವಾರ.]] ಒಲೆಸ್ಕೊ ಕೋಟೆ, ತೆತ್ತಿದ ಆಕಾರ, ಒಂಜಿ ಎಲ್ಯ ಗುಡ್ಡೆದ ಮಿತ್ತ್ ಉಂಡು, ಸುಮಾರ್ ಐವತ್ತು ಮೀಟರ್ ಎತ್ತರೊಡು ಉಂಡು. ಒಂಜಿ ಕಂದಕ ಬೊಕ್ಕ ಒಂಜಿ ಗೋಡೆ ಅಯಿನ್ ಸುತ್ತುವರಿದ್ ಉಂಡು, ಉಂದು ಕೋಟೆಗ್ ರಕ್ಷಣೆ ಆದ್ ಸೇವೆ ಮಲ್ಪುಂಡು. ಕೋಟೆನ್ ಒಂಜಿ ದಟ್ಟ ಜವುಗುಲಾ ಸುತ್ತುವರಿದ್ ಉಂಡು. ಕೋಟೆ ಇತ್ತಿನ ಜಮಿನ್ ಮಸ್ತ್ ಸರ್ತಿ ಮಾಲೀಕತ್ವೊನು ಬದಲಾತುಂಡು. ಉಂದು ಮೂಲತಃ ವೊಲ್ಹಿನಿಯಾ ಬೊಕ್ಕ ಲ್ವಿವ್ ದೇಶದ ಗಡಿಟ್ ಇತ್ತುಂಡು. ಈ ಕೋಟೆ ಬೇತೆ ಬೇತೆ ಕಾಲೊಡು ಪೋಲೆಂಡ್, ಲಿಥುವೇನಿಯಾ ಬೊಕ್ಕ ಹಂಗೇರಿ ದೇಶೊಲೆನ ಒಟ್ಟುಗು ಇತ್ತ್ಂಡ್. 14ನೆ ಶತಮಾನೊಡು ದುಂಬು ಪನ್ಪಿನ ಮೂಜಿ ದೇಶೊಲೆನ ನಡುಟು ಚಲನಶೀಲ ಗಡಿಲು ಅಯಿತ ಪ್ರದೇಶೊಡು ನಡತೊಂದು ಪೋನಗ ಉಂದು ಒಂಜಿ ರಾಜಕೀಯ ಮೈಲುಗಲ್ಲು ಆಂಡ್. ಕೋಟೆದ ಒಡೆತನೊಗು ನಡಪುನ ಯುದ್ಧೊಲು ನಿರಂತರವಾದ್ ನಡತೊಂದು ಇತ್ತುಂಡು. ಕೋಟೆದ ನೆಲಮಾಳಿಗೆಡು ಒಂಜಿ ಆಳೊದ ಬಾವಿನ್ ಮುತ್ತಿಗೆಡ್ ಉಪ್ಪುನ ನಿವಾಸಿಲೆಗ್ ತಪ್ಪಿಸಿಕೊಳ್ಳುನ ಮಾರ್ಗವಾದ್ ಬಳಕೆ ಮಲ್ತೆರ್. == ನಿವಾಸಿಲು == 15ನೆ ಶತಮಾನೊಡು, ಕೋಟೆನ್ ರಕ್ಷಣಾ ಜಾಗ ಕುಲೀನ ವರ್ಗದ ಒಂಜಿ ಪಾರಾಯಣ ಆದು ಬದಲಾವಣೆ ಮಲ್ತೆರ್. 1605ನೇ ಇಸವಿಡ್, ರುಥೇನಿಯನ್ ಪ್ರಾಂತ್ಯದ ಅಧಿಕಾರಿ ವಾಯ್ವೋಡೆ, ಅಂಚನೆ ಮುಲ್ಪದ ಒರಿ ಶ್ರೀಮಂತ ಭೂಮಾಲಿಕೆ, ಕುಲೀನ ವ್ಯಕ್ತಿ 'ಜಾನ್ ಡಾನಿಲೋವಿಚ್ ಹೆಚ್. ಸಾಸ್' ಪನ್ಪಿನಾರ್ ಈ ಕೋಟೆನ್ ಗತೊನುವೆರ್. ಬೊಕ್ಕ ಉಂದೆನ್ 'ಕೊನಿಯೆಕ್ ಪೋಲ್ಸ್ಕಿ' ಕುಟುಂಬೊಗು ಮಾರುದು ಕೊರ್ಪೆರ್. 'ಝೆವುಸ್ಕಿ'ಡ್ ಮುಕ್ಯ ನೆಲೆ ಇತ್ತಿನ ಪೊಸ ಯಜಮಾನೆರ್ ಈ ಕೋಟೆನ್ ಸರಿಟು ತೂವೊನಂದೆ ನಿರ್ಲಕ್ಷ್ಯ ಮಲ್ತುದು ಹಾಳ್ ಮಲ್ಪುವೆರ್. ಕಡೆಕ್ 1682ಡ್, 'ಮೂರನೇ ಜಾನ್ ಸೋಬಿಸ್ಕಿ' ಪನ್ಪಿನಾರ್ 'ಸ್ಟಾನಿಸ್ಲಾವ್ ಕೊನಿಯೆಕ್ ಪೋಲ್ಸ್ಕಿ'ಡ್ದ್ 4,00,000 ಝ್ಲೋಟಿಗೆ ಈ ಕೋಟೆನ್ ಗೇತೊಂದು ಕುಡ ಪೊಸತ್ತ್ ಮಲ್ಪುವೆರು. 18ನೇ ಶತಮಾನದ ಸುರುಟು, ಒಲೆಸ್ಕೊ ಕೋಟೆನ್ 'ಝೆವುಸ್ಕಿ' ಕುಟುಂಬದಕುಲು ಗೇತೊನುವೆರ್. ಅಂಚನೆ ಮುಲ್ಪ ಇತ್ತಿನ ಪಿರಾಕ್‌ದ ವಸ್ತುಲೆನ್ ಅಕುಲೆಗ್ ಸೇರ್ದಿನ ನನೊಂಜಿ ಕೋಟೆ ಆಯಿನ 'ಪಿಧೀರ್ಟ್ಸಿ ಕೋಟೆ'ಗ್ ಕೊನೊಪೆರ್. 'ವಿಯೆನ್ನಾ ಯುದ್ಧ'ದ ವೀರ ಆಯಿನ ಪೋಲಿಷ್ ರಾಜ 'ಮೂಜನೇ ಜಾನ್ ಸೋಬಿಸ್ಕಿ' ಪುಟ್ಟಿನ ಜಾಗ ಉಂದು. ಪನ್ಪಿನೆಕ್ ಈ ಕೋಟೆ ಮಸ್ತ್ ಪುದರ್ ಪಡೆತುಂಡು. ಆರ್ ಎಚ್ಚಾದ್ ಮುಲ್ಪನೇ ವಾಸ ಮಲ್ತೊಂದಿತ್ತೆರ್, ಅಂಚನೆ ಇತ್ತೆದ ಮ್ಯೂಸಿಯಮ್‌ ದರ್ಶನೊಗು ದೀತಿನ ಮಸ್ತ್ ಕಲಾಕೃತಿಲೆನ್ ಆರ್ ಒಟ್ಟು ಮಲ್ತುದಿತ್ತೆರ್. ನನೊರಿ ಪೋಲಿಷ್ ರಾಜ ಆಯಿನ 'ಮಿಖಾಲ್ ಕೊರಿಬುಟ್ ವಿಸ್ನಿಯೋವಿಯೆಕ್ಕಿ'ಲಾ ಮುಲ್ಪನೇ ಪುಟ್ಟಿನಾರ್. ಉಕ್ರೇನಿಯನ್ ಕೊಸಾಕ್‌ಲೆನ ಬಾರಿ ಪ್ರಸಿದ್ಧ ನಾಯಕೆ ಆಯಿನ 'ಬೋಹ್ಡಾನ್ ಖ್ಮೆಲ್ನಿಟ್ಸ್ಕಿ'ಲಾ ತನ್ನ ಬಾಲ್ಯದ ದಿನೊಕುಲೆನ್ ಮುಲ್ಪನೇ ಕಳೆಯೆರ್ ಪನ್ಪಿನ ಪಾತೆರ ಉಂಡು. [[ಫೈಲ್:Inside_Olesko_Castle.jpg|thumb|225x225px| ಒಲೆಸ್ಕೊ ಕೋಟೆದ ಒಳಾಂಗಣ ಅಂಗಳ.]] == ಪುನಃಸ್ಥಾಪನೆಲು == ಈ ಕೋಟೆನ್ 16ನೆ ಶತಮಾನೊದ ಕಡೆಟ್ 17ನೆ ಶತಮಾನೊದ ಸುರುಟು ಪುನರ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. ಕೋಟೆದ ಬೇತೆ ಬೇತೆ ಕೊಠಡಿಲೆನ್ ಅಲಂಕರಿಸೆರೆ ಚಿತ್ರಕಲೆ ಬೊಕ್ಕ ಮೊಸಾಯಿಕ್ ಲೆನ್ ಕನತೆರ್. ಆ ಕಾಲೊಡು ಜನಪ್ರಿಯ ಆಯಿನ ಇಟಾಲಿಯನ್ ನವೋದಯ ಶೈಲಿಡ್ ಈ ಕೋಟೆನ್ ಪುನರ್ನಿರ್ಮಾಣ ಮಲ್ತೆರ್. 1838ಡ್, ಒಂಜಿ ಭೂಕಂಪ ಕೋಟೆನ್ ಕಂಪೊಡು, ಕೆಲವು ಪ್ರದೇಶೊಲೆನ್ ಭಾಗಶಃ ನಾಶ ಮಲ್ತ್ಂಡ್. 1882ಡ್, ಪೋಲಿಷ್ ರಾಷ್ಟ್ರೀಯ ಸ್ಮಾರಕ ಪಂಡ್ದ್ ಪರಿಗಣಿಸಾಯಿನ ಕೋಟೆನ್ ಒಲೆಸ್ಕೊ ಕೋಟೆದ ಸಂರಕ್ಷಣೆ ಸಮಿತಿ ಖರೀದಿ ಮಲ್ತ್ಂಡ್, ಉಂದು 1892ಡ್ ಜೀರ್ಣೋದ್ಧಾರೊಗು ಕಾರಣವಾತ್ಂಡ್. ಸುರುತ ವಿಶ್ವ ಯುದ್ಧ ಬೊಕ್ಕ ರಡ್ಡನೆ ವಿಶ್ವ ಯುದ್ಧ ರಡ್ಡ್ ಲಾ ಕೋಟೆದ ಮಿತ್ತ್ ಕೆಟ್ಟ ಪರಿಣಾಮ ಬೀರುಂಡು, ದುಂಬುದ ಜೀರ್ಣೋದ್ಧಾರದ ಬೇಲೆನ್ ರದ್ದು ಮಲ್ಪುಂಡು. 1956ಡ್ ಕೋಟೆಗ್ ಮಿಂಚು ಬತ್ತ್ಂಡ್. ಕೋಟೆನ್ ಪುನರ್ ನಿರ್ಮಾಣ ಮಲ್ತೆರ್, ೧೯೬೧ಡ್ದ್ ಸುರು ಆದ್ ೧೯೮೫ ಮುಟ್ಟ ನಡತ್ಂಡ್. ಇನಿ, ಉಂದು ಒಂಜಿ ಮ್ಯೂಸಿಯಂ ಆದುಂಡು, 16-17ನೆ ಶತಮಾನೊದ ಪ್ರಾಚೀನ ಫರ್ನಿಶಿಂಗ್ ಬೊಕ್ಕ ಕಲೆತ ಸಂಗ್ರಹೊಲೆನ್ ಪ್ರದರ್ಶಿಸವುಂಡು. ಉಂದೆಟ್ ಶಿಲ್ಪೊಲು, ಚಿತ್ರಕಲೆಲು, ಸ್ಟಿಲ್ ಲೈವ್ಸ್, ಅಪ್ಲೈಡ್ ಆರ್ಟ್ಸ್, ಟ್ಯಾಪೆಸ್ಟ್ರಿಲು, ಕಾಲೊದ ಶಸ್ತ್ರಾಸ್ತ್ರೊಲು, ಬೊಕ್ಕ ಆ ಕಾಲೊಡು ನಿತ್ಯಜೀವನೊಡು ಬಳಕೆ ಆಪಿನ ವಸ್ತುಲುಲಾ ಉಂಡು. ಪೋಲೆಂಡ್ ದ ಗಡಿತ ಪಿದಯಿ ಪೋಲಿಷ್ ಕಲೆತ ಅತ್ಯಂತ ಶ್ರೀಮಂತ ಖಜಾನೆಲೆಡ್ ಒಂಜಿ ಪಂಡ್ದ್ ಅಯಿತ ಸಂಗ್ರಹೊನು ಪರಿಗಣಿಸವೊಂಡುಂಡು. ಈ ಕೋಟೆ "ಗೋಲ್ಡನ್ ಹಾರ್ಸ್ಶೂ" ದ ಒಂಜಿ ಭಾಗ, ಒಂಜೊಂಜಿ ಸಮೀಪೊಡು ಉಪ್ಪುನ ಮೂಜಿ ಕೋಟೆಲೆನ ಒಂಜಿ ಉಂಗುರ: ಒಲೆಸ್ಕೊ, ಪಿಧಿರ್ಸಿ, ಬೊಕ್ಕ ಜೊಲೊಚಿವ್ ಕೋಟೆಲು.<gallery perrow="5" caption="ಕೋಟೆಡ್ದ್ ಚಿತ್ರಕಲೆಲು"> ಫೈಲ್:Boguszowicz_Taniec_Salome.jpg|alt=Dance of Salome, Szymon Boguszowicz| ''ಸಲೋಮೆನ ನಲಿಕೆ'', [[Szymon Boguszowicz|ಸ್ಜಿಮನ್ ಬೊಗುಸ್ಜೊವಿಕ್ಸ್]] ಫೈಲ್:Siemiginowski_Bachus_i_Ariadna.jpg|alt=Bacchus and Ariadne, Jerzy Siemiginowski-Eleuter| ''ಬ್ಯಾಕಸ್ ಬೊಕ್ಕ ಅರಿಯಡ್ನೆ'', ಜೆರ್ಜಿ ಸೀಮಿಗಿನೊವ್ಸ್ಕಿ-ಎಲೆಟರ್ ಫೈಲ್:Czechowicz_Dzieciątko_Jezus_wśród_aniołów.jpg|alt=The Christ Child sleeping among angels, Szymon Czechowicz| ''ದೇವದೂತೆರೆನ ನಡುಟು ನಿದ್ರೆ ಮಲ್ಪುನ ಕ್ರಿಸ್ತ ಬಾಲೆ'', ಸ್ಜಿಮನ್ ಚೆಕೊವಿಕ್ಸ್ ಫೈಲ್:Michał_Józef_Rzewuski.jpg|alt=Portrait of Michał Józef Rzewuski, Marcello Bacciarelli| ''ಮಿಚಾಲ್ ಜೋಜೆಫ್ ರ್ಜೆವುಸ್ಕಿ, ಮಾರ್ಸೆಲ್ಲೊ ಬ್ಯಾಸಿಯರೆಲ್ಲಿನ ಭಾವಚಿತ್ರ'' ಫೈಲ್:Chojnicki_Portret_damy.jpg|alt=Portrait of a lady, Józef Chojnicki| ''ಒರಿ ಪೊಣ್ಣುನ ಭಾವಚಿತ್ರ'', [[Józef Chojnicki|ಜೋಜೆಫ್ ಚೊಜ್ನಿಕ್ಕಿ]] </gallery> == ಫಿಲಾಟೆಲಿ ಬೊಕ್ಕ ನಾಣ್ಯಶಾಸ್ತ್ರೊಡು == ಜೂನ್ ೯, ೨೦೧೭ಡ್, ರಾಷ್ಟ್ರೀಯ ಅಂಚೆ ಆಪರೇಟರ್ ಉಕ್ರ್ಪೊಷ್ಟಾ ಯುಎಎಚ್ ೫.೮೦ ದ ಮುಖಮೌಲ್ಯೊದ "ಒಲೆಸ್ಕಿ ಕ್ಯಾಸಲ್ ಆಫ್ ದ XIII-XVIII ಸೆಂಚುರಿಸ್" ಪನ್ಪಿನ ಒಂಜಿ ಅಂಚೆ ಚೀಟಿನ್ ಚಲಾವಣೆಗ್ ಪಾಡ್ದೆರ್. ಸಿಇಪಿಟಿ ಕಾರ್ಯಕ್ರಮದ ಅಡಿಟ್ ಬಿಡುಗಡೆ ಆಯಿನ ಈ ಚೀಟಿ, ಯುರೋಪಿಯನ್ ಅಂಚೆ ಆಪರೇಟರ್ ಲೆನ ಸಂಘ ಪೋಸ್ಟ್ ಯುರೋಪ್ ನಡಪಾಯಿನ ವಾರ್ಷಿಕ ಅಂಚೆ ಚೀಟಿ ರೇಟಿಂಗ್ ಡ್ ಉಕ್ರೇನ್ ನ್ ಪ್ರತಿನಿಧಿಸವುಂಡು. ಆಗಸ್ಟ್ ೧೭, ೨೦೨೧ಡ್, ನ್ಯಾಷನಲ್ ಬ್ಯಾಂಕ್ ಆಫ್ ಉಕ್ರೇನ್ ೧೦-ಹೈವ್ನಿಯಾ ಬೊಳ್ಳಿದ ಸ್ಮರಣಾರ್ಥ ನಾಣ್ಯ "ಒಲೆಸ್ಕಿ ಕ್ಯಾಸಲ್" ನ್ ಚಲಾವಣೆಗ್ ಪಾಡ್ಂಡ್. ಉಂದು "ಉಕ್ರೇನ್ ದ ವಾಸ್ತುಶಿಲ್ಪದ ಸ್ಮಾರಕೊಲು" ಪನ್ಪಿನ ಸರಣಿಗ್ ಸೇರ್ದುಂಡು. == ಅಂಚನೆ ತೂಲೆ == * ಉಕ್ರೇನ್ ಡ್ ಉಪ್ಪುನ ಕೋಟೆಲೆನ ಪಟ್ಟಿ <templatestyles src="Module:Side box/styles.css"></templatestyles>{{Commons category|Olesko Castle}} == ಉಲ್ಲೇಖೊಲು == {{Reflist}} == ಬಾಹ್ಯ ಕೊಂಡಿಲು == * [http://www.wilanow-palac.pl/the_olesko_castle.html ವಿಲಾನೋವ್ ಅರಮನೆದ ಮ್ಯೂಸಿಯಂಡ್ ಒಲೆಸ್ಕೊ ಕೋಟೆನ್] 2016-03-03 ಡ್ Archived * [http://www.grydnycia.lviv.ua/en/ ಒಲೆಸ್ಕೊ ಕೋಟೆದ ಒಳಗೆ ಮಧ್ಯಕಾಲೀನ ರೆಸ್ಟೋರೆಂಟ್ ಹ್ರಿಡ್ನಿಟ್ಸ್ಯ] * [http://www.lvivecotour.com/index.php?/tours/castles/ lvivecotour.com] 2011-09-28 ಗ್ Archived – ಒಲೆಸ್ಕೊ ಕೋಟೆದ ಪ್ರವಾಸ ಇಂಗ್ಲಿಷ್ ಡ್ {{Historic reserves of Lviv Oblast}}{{Kresy Castles}}{{Castles in Ukraine}} [[ವರ್ಗೊ:ಪರಿಶೀಲಿಸಂದಿನ ಅನುವಾದೊಲು ಇಪ್ಪುನ ಪುಟೊಕುಲು]] c83acenzsle1y80ppn9e1enbsh6l9vh